# LinkSyncExtension - Design Documentation ## Architecture Overview LinkSyncExtension is a Firefox browser extension that synchronizes bookmarks with LinkSyncServer. It runs as a background service worker with popup and settings interfaces. ### Component Architecture ``` ┌─────────────────────────────────────────────────────┐ │ LinkSyncExtension │ │ │ │ ┌─────────────────┐ ┌─────────────────────────┐ │ │ │ Background │ │ Popup UI │ │ │ │ Service │ │ (Add/Edit Bookmarks) │ │ │ │ Worker │ │ │ │ │ └─────────────────┘ └─────────────────────────┘ │ │ ┌─────────────────┐ ┌─────────────────────────┐ │ │ │ Storage │ │ Settings UI │ │ │ │ Manager │ │ (Configuration) │ │ │ └─────────────────┘ └─────────────────────────┘ │ │ ┌─────────────────┐ ┌─────────────────────────┐ │ │ │ Sync Engine │ │ Query Engine │ │ │ │ (3 modes) │ │ (Parser + Executor) │ │ │ └─────────────────┘ └─────────────────────────┘ │ │ ┌─────────────────┐ ┌─────────────────────────┐ │ │ │ Bookmark │ │ API Client │ │ │ │ Manipulator │ │ (REST calls) │ │ │ └─────────────────┘ └─────────────────────────┘ │ │ │ │ ┌───────────────────────────────────────────┐ │ │ │ Browser Bookmarks API │ │ │ └───────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────┘ ``` ## File Structure ``` LinkSyncExtension/ ├── manifest.json # Extension manifest v2 ├── popup.html # Bookmark add/edit UI ├── popup.css # Popup styling ├── popup.js # Popup logic ├── background.html # Settings page ├── background.js # Service worker ├── content/ │ └── content.js # Content script (optional) ├── utils/ │ ├── bookmark.js # Bookmark manipulation │ ├── collection.js # Collection management │ ├── query-engine.js # Query parsing/execution │ └── sync.js # Sync logic ├── icons/ │ ├── icon-48.png # 48x48 icon │ └── icon-96.png # 96x96 icon └── styles/ ├── base.css # Common styles └── theme.css # Theme variables ``` ## Manifest Design ### manifest.json ```json { "manifest_version": 2, "name": "LinkSync", "version": "1.0.0", "description": "Sync bookmarks with LinkSyncServer", "permissions": [ "bookmarks", "storage", "activeTab", "http://*/*", "https://*/*" ], "browser_action": { "default_icon": { "48": "icons/icon-48.png", "96": "icons/icon-96.png" }, "default_title": "LinkSync" }, "background": { "page": "background.html" }, "browser_specific_settings": { "gecko": { "id": "{linksync-id}", "strict_min_version": "109.0" } } } ``` ### Permissions - `bookmarks` - Read/write Firefox bookmarks - `storage` - Store settings, API keys, state - `activeTab` - Get current page data - HTTP/HTTPS - API communication ## Background Worker Design ### Responsibilities 1. **Sync Loop** - Check for pending syncs - Compare browser vs server bookmarks - Apply sync mode rules - Handle conflicts 2. **Event Handlers** - `onMessage` - UI requests - `onInstall` - Initialization - `onUpdate` - Handle version changes 3. **State Management** - Store collection mapping - Track sync timestamps - Monitor pending changes ### Code Structure ```javascript // background.js const Background = { // Constants SYNC_CHECK_INTERVAL: 60000, // 1 minute // Storage keys STORAGE: { API_KEY: 'linksync_api_key', COLLECTION: 'linksync_collection', MODE: 'linksync_sync_mode', DELETIONS: 'linksync_deletions', AUTO_SYNC: 'linksync_auto_sync' }, // Methods init(), // Initialize on install/update checkSync(), // Run sync loop handleSyncAction(), // Process sync actions handleEvent(), // Event handlers sendMessage(), // UI communication authenticate() // Handle auth }; ``` ### Sync Logic ```javascript async function handleSync() { const config = await loadConfig(); // Get browser bookmarks const browserBookmarks = await getBrowserBookmarks(); // Get server bookmarks via API const serverBookmarks = await fetchServerBookmarks(); // Apply sync mode const actions = applySyncMode(config.mode, browserBookmarks, serverBookmarks); // Process deletions if enabled if (config.deletions) { actions = applyDeletions(actions); } // Apply actions await applyActions(actions); // Update sync timestamp await saveSyncTimestamp(); } ``` ### Sync Modes | Mode | Browser→Server | Server→Browser | |------|---------------|---------------| | **Bi-directional** | Push | Push | | **Browser Authoritative** | Push | Overwrite | | **Server Authoritative** | Download | Overwrite | ## Popup Design ### Components 1. **Add/Edit Form** - URL (auto-filled) - Title (auto-filled) - Description (auto-filled) - Notes - Tags input - Folder path - Actions (Add, Edit, Delete) 2. **Bookmark List** - Paginated list of synced bookmarks - Search filter - Select for batch operations 3. **Collections Panel** - View all collections - Execute query - Create dynamic collection 4. **Settings Modal** - Server URL - API Key - Collection name - Sync mode - Auto-sync toggle ### HTML Structure ```html

LinkSync

``` ## Storage Design ### localStorage Keys | Key | Type | Description | |-----|------|-------------| | `linksync_api_key` | string | JWT API token | | `linksync_collection` | string | Collection name | | `linksync_sync_mode` | string | Sync mode | | `linksync_deletions` | boolean | Enable deletions | | `linksync_auto_sync` | boolean | Auto-sync toggle | | `linksync_last_sync` | timestamp | Last sync time | | `linksync_pending` | number | Pending changes count | ### Encrypted Storage API keys should be encrypted before storage: ```javascript async function saveEncryptedKey(key) { const iv = crypto.getRandomValues(new Uint8Array(16)); const encrypted = await window.crypto.subtle.encrypt( { name: "AES-GCM", length: 256 }, await window.crypto.subtle.importKey( "raw", encryptionKey, { name: "AES-GCM" }, false ), key ); // Store iv + encrypted data } ``` ## Query Engine Design ### Query Syntax ``` ('term1', 'term2') OR tagA AND tagB XOR url:example.com ``` ### Parser ```javascript class QueryParser { parse(expression) { // Tokenize const tokens = this.tokenize(expression); // Build AST const ast = this.buildAST(tokens); // Validate this.validate(ast); return this.serialize(ast); } } ``` ### Executor ```javascript class QueryExecutor { async execute(ast) { // Build SQL const sql = this.buildSQL(ast); // Execute const result = await fetch(`/api/links/?sql=${sql}`); return await result.json(); } } ``` ## API Client Design ### REST API Integration ```javascript const API = { baseUrl: '', headers: { 'Authorization': 'Token {key}', 'Content-Type': 'application/json' }, async login() { const response = await fetch(`${this.baseUrl}/api/auth/login/`, { method: 'POST', body: JSON.stringify({ username, password }) }); const data = await response.json(); this.storeApiKey(data.token); return data; }, async listLinks() { return await this.request('/api/links/'); }, async createLink(link) { return await this.post('/api/links/', link); }, async executeQuery(expression) { return await this.post('/api/queries/execute/', { expression }); } }; ``` ## UI/UX Design ### Color Scheme ```css :root { --primary: #3b82f6; --secondary: #6b7280; --success: #10b981; --warning: #f59e0b; --error: #ef4444; --background: #ffffff; --surface: #f9fafb; --border: #e5e7eb; --text: #111827; --text-secondary: #6b7280; } ``` ### Typography - Font family: System UI - Base size: 14px - Heading: 18px - Form labels: 12px ### Responsive Design - Mobile-first approach - Breakpoint: 480px for landscape - Touch-friendly tap targets (44px minimum) ## Security Design ### API Key Handling 1. **Storage** - Encrypted in localStorage - Never logged or exposed 2. **Transmission** - HTTPS preferred - Token in Authorization header - No tokens in URL params 3. **Validation** - Verify response signatures - Check rate limits - Handle 401/403 gracefully ### Data Privacy - No bookmarks stored locally after sync - API keys user-managed - No telemetry or analytics ## Testing Strategy ### Unit Tests - Sync logic modes - Conflict detection - Query parsing - Storage operations ### Integration Tests - API endpoint calls - Background worker events - Popup communication ### Manual Testing - Add/edit/delete bookmarks - Collection creation - Query execution - Conflict scenarios