# Milestone 12: Browser Extension ## Overview Browser extension to save content from any webpage directly to Cortex. One-click capture for research and documentation. ## Motivation - Web research is a major knowledge source - Manual copy-paste is friction - Supermemory's browser extension is popular - Capture context while browsing ## Features ### 12.1 Quick Save Right-click any page → "Save to Cortex" ### 12.2 Selection Save Select text → Right-click → "Save selection to Cortex" ### 12.3 Popup Interface ``` ┌─────────────────────────────────────────┐ │ 🧠 Save to Cortex │ ├─────────────────────────────────────────┤ │ Title: [API Documentation ] │ │ Kind: [memory ▼] │ │ Tags: [docs, api, reference ] │ │ │ │ ☑ Include page content │ │ ☐ Include selection only │ │ ☐ Include screenshot │ │ │ │ [Save] [Cancel] │ └─────────────────────────────────────────┘ ``` ### 12.4 Auto-Extract Automatically extract: - Page title - Main content (via Readability) - Meta description - Author/date if available ### 12.5 Tag Suggestions Suggest tags based on: - Page content analysis - Existing tags in Cortex - URL domain ### 12.6 Local Communication Extension communicates with local Cortex server: - Native messaging (preferred) - Local HTTP API fallback ## Implementation ### Extension Structure ``` extension/ ├── manifest.json ├── popup/ │ ├── popup.html │ ├── popup.js │ └── popup.css ├── content/ │ └── content.js # Injected into pages ├── background/ │ └── background.js # Service worker ├── icons/ │ ├── icon-16.png │ ├── icon-48.png │ └── icon-128.png └── native/ └── cortex-native.js # Native messaging host ``` ### Manifest (v3) ```json { "manifest_version": 3, "name": "Cortex - Save to Memory", "version": "1.0.0", "description": "Save web content to your Cortex knowledge graph", "permissions": [ "activeTab", "contextMenus", "storage", "nativeMessaging" ], "host_permissions": [ "http://localhost:3100/*" ], "action": { "default_popup": "popup/popup.html", "default_icon": { "16": "icons/icon-16.png", "48": "icons/icon-48.png", "128": "icons/icon-128.png" } }, "background": { "service_worker": "background/background.js" }, "content_scripts": [ { "matches": [""], "js": ["content/content.js"] } ] } ``` ### Content Script ```javascript // content/content.js // Extract page content using Readability function extractContent() { const clone = document.cloneNode(true); const reader = new Readability(clone); const article = reader.parse(); return { title: article?.title || document.title, content: article?.textContent || '', excerpt: article?.excerpt || '', url: window.location.href, selection: window.getSelection()?.toString() || '', }; } // Listen for messages from popup/background chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === 'extract') { sendResponse(extractContent()); } }); ``` ### Background Script ```javascript // background/background.js // Context menu chrome.runtime.onInstalled.addListener(() => { chrome.contextMenus.create({ id: 'save-to-cortex', title: 'Save to Cortex', contexts: ['page', 'selection'], }); }); chrome.contextMenus.onClicked.addListener(async (info, tab) => { if (info.menuItemId === 'save-to-cortex') { // Get content from page const [{ result }] = await chrome.scripting.executeScript({ target: { tabId: tab.id }, func: () => window.__cortexExtract(), }); // Save via native messaging or HTTP await saveToCortext(result); } }); async function saveToCortex(content) { try { // Try native messaging first const response = await chrome.runtime.sendNativeMessage('cortex', { action: 'save', data: content, }); return response; } catch { // Fall back to HTTP API const response = await fetch('http://localhost:3100/api/nodes', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ kind: 'memory', title: content.title, content: content.content, tags: ['web-clip'], metadata: { source: { type: 'url', url: content.url }, }, }), }); return response.json(); } } ``` ### Popup UI ```html

🧠 Save to Cortex

``` ### Native Messaging Host ```javascript // native/cortex-native.js #!/usr/bin/env node const { addNode } = require('../dist/core/store'); process.stdin.on('readable', () => { // Read message length (4 bytes) const lengthBuf = process.stdin.read(4); if (!lengthBuf) return; const length = lengthBuf.readUInt32LE(0); const messageBuf = process.stdin.read(length); const message = JSON.parse(messageBuf.toString()); handleMessage(message).then(response => { const responseBuf = Buffer.from(JSON.stringify(response)); const lengthBuf = Buffer.alloc(4); lengthBuf.writeUInt32LE(responseBuf.length, 0); process.stdout.write(lengthBuf); process.stdout.write(responseBuf); }); }); async function handleMessage(message) { if (message.action === 'save') { const node = await addNode({ kind: message.data.kind || 'memory', title: message.data.title, content: message.data.content, tags: message.data.tags || ['web-clip'], metadata: { source: message.data.source }, }); return { success: true, nodeId: node.id }; } return { success: false, error: 'Unknown action' }; } ``` ## CLI Commands | Command | Description | |---------|-------------| | `cortex extension install` | Install native messaging host | | `cortex extension status` | Check extension connectivity | ## Testing - [ ] Extension installs in Chrome/Edge - [ ] Context menu appears - [ ] Popup opens and pre-fills data - [ ] Save creates node in Cortex - [ ] Selection save captures selected text - [ ] Native messaging works - [ ] HTTP fallback works ## Acceptance Criteria - [ ] One-click save from any webpage - [ ] Content extracted cleanly - [ ] Tags can be added before save - [ ] Works without Cortex server running (queues) - [ ] Firefox + Chrome support ## Estimated Effort - Extension scaffold: 2 hours - Content extraction: 3 hours - Popup UI: 4 hours - Background worker: 3 hours - Native messaging: 4 hours - Testing/polish: 4 hours - **Total: ~20 hours** ## Dependencies - `@anthropic/readability` for content extraction - Chrome/Edge extension APIs ## References - [Chrome Extension Manifest V3](https://developer.chrome.com/docs/extensions/mv3/intro/) - [Native Messaging](https://developer.chrome.com/docs/extensions/mv3/nativeMessaging/) - [Readability](https://github.com/mozilla/readability)