Files
myworkspace/LinkSyncExtension/design.md

449 lines
12 KiB
Markdown

# 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
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<!-- Header -->
<header>
<h1>LinkSync</h1>
</header>
<!-- Add/Edit Form -->
<section id="bookmark-form">
<form id="bookmark-form">
<input id="url" type="url">
<input id="title" type="text">
<textarea id="description"></textarea>
<textarea id="notes"></textarea>
<input id="tags" placeholder="comma-separated">
<input id="folder" placeholder="path">
<button id="submit">Add Bookmark</button>
</form>
</section>
<!-- Bookmark List -->
<section id="bookmark-list">
<!-- Bookmarks -->
</section>
<!-- Footer with actions -->
<footer>
<button id="sync-btn">Sync Now</button>
<button id="settings-btn">Settings</button>
</footer>
</body>
</html>
```
## 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