414 lines
11 KiB
Markdown
414 lines
11 KiB
Markdown
# LinkdingSync Extension - Design Document
|
|
|
|
## Overview
|
|
|
|
`linkdingsync` is a Firefox browser extension that synchronizes bookmarks with a self-hosted Linkding instance. It supports folder hierarchy, bi-directional sync, configurable sync modes, and tag-based bundle management.
|
|
|
|
## Project Location
|
|
|
|
```
|
|
Linkding Browser Extension/LinkdingSync/
|
|
```
|
|
|
|
## Extension Name
|
|
|
|
**`linkdingsync`** - Firefox extension name (manifest.json)
|
|
|
|
---
|
|
|
|
## 1. Data Storage Structure
|
|
|
|
### Configuration (stored in browser.storage.local)
|
|
|
|
```json
|
|
{
|
|
"serverUrl": "https://links.blabber1565.com",
|
|
"apiKey": "your-api-token-here",
|
|
"bundleTag": "bundle_personal_firefox_1",
|
|
"syncMode": "bi-directional",
|
|
"autoGenerateTags": false,
|
|
"lastSyncTimestamp": 1715012345678
|
|
}
|
|
```
|
|
|
|
### Storage Areas
|
|
|
|
- **`browser.storage.local`** - Persistent data per browser profile
|
|
- **`browser.storage.session`** - Temporary session data
|
|
|
|
### Supported Values
|
|
|
|
| Value | Description |
|
|
|-------|------|
|
|
| `bi-directional` | Replicate both directions; keep both versions |
|
|
| `write-only` | Browser is authoritative source; update Linkding only |
|
|
| `read-only` | Linkding is authoritative source; download only |
|
|
|
|
**Default:** `bi-directional`
|
|
|
|
---
|
|
|
|
## 2. Bookmark Notes Structure
|
|
|
|
The `notes` field uses a JSON-compatible structure with versioning for forward compatibility:
|
|
|
|
```json
|
|
{
|
|
"version": 1.0,
|
|
"path": "Work/Resources/Development",
|
|
"userNotes": "Development resources and references",
|
|
"autoTags": [
|
|
{"name": "Work"},
|
|
{"name": "Resources"},
|
|
{"name": "Development"}
|
|
],
|
|
"bundleTag": "bundle_personal_firefox_1"
|
|
}
|
|
```
|
|
|
|
### Display Format
|
|
- Only show `userNotes` in the UI
|
|
- `path` and `autoTags` are hidden from regular users
|
|
|
|
### Version Handling
|
|
|
|
- **version 1.0**: Basic structured format with path, userNotes, autoTags
|
|
- **version 2.0**: Adds bundleTag field for tag-based bundle identification
|
|
- **Extension version tracking**: Uses extension version number for notes version
|
|
- **Backward compatibility**:
|
|
- Old bookmarks without structured notes are detected
|
|
- Non-JSON notes are migrated to structured format
|
|
- Old notes get `version: 1.0` and existing text in `userNotes`
|
|
- Future fields are ignored by older versions
|
|
|
|
### Auto-generate Tags Setting
|
|
- **Default:** `false` (disabled)
|
|
- When `true`, extracts folder names from `path` as tag suggestions
|
|
- User must organize folders intentionally for tag auto-generation to work correctly
|
|
|
|
---
|
|
|
|
## 3. Bundle/Tag Configuration
|
|
|
|
Instead of creating actual Linkding bundles, we use Linkding's "required tags" feature:
|
|
|
|
### Bundle Tag Convention
|
|
|
|
All bookmarks in a bundle must have the bundle tag. The bundle tag name follows this pattern:
|
|
```
|
|
bundle_{BUNDLE_NAME}_{BROWSER}
|
|
```
|
|
|
|
Examples:
|
|
- `bundle_personal_firefox_1`
|
|
- `bundle_work_chrome_1`
|
|
- `bundle_personal_edge_1`
|
|
|
|
### Linkding Query Parameters
|
|
|
|
In Linkding, create a bundle with these parameters:
|
|
- **Search:** (empty)
|
|
- **Required tags:** `{bundleTag}`
|
|
- **Any tags:** (empty)
|
|
- **All tags:** (empty)
|
|
- **Excluded tags:** (empty)
|
|
|
|
This means all bookmarks with the bundle tag appear in this bundle.
|
|
|
|
### Updating Extension Version
|
|
|
|
The extension version is used as the notes version:
|
|
- Extension v1.0.x → notes version 1.0
|
|
- Extension v2.0.x → notes version 2.0
|
|
- Extension v1.0.1 → notes version 1.0 (patch versions don't change notes version)
|
|
|
|
---
|
|
|
|
## 4. Extension File Structure
|
|
|
|
```
|
|
LinkdingSync/
|
|
├── manifest.json # Firefox manifest v2
|
|
├── popup.html # Main extension popup
|
|
├── popup.css # Popup styling
|
|
├── popup.js # Popup UI logic
|
|
├── background.html # Settings/config page
|
|
├── background.js # Service worker
|
|
├── README.md # User documentation
|
|
├── docs/
|
|
│ ├── design.md # Design document
|
|
│ └── TODOs.txt # Version compatibility notes
|
|
└── icons/
|
|
├── icon-48.svg # 48x48 icon
|
|
└── icon-96.svg # 96x96 icon
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Key Features
|
|
|
|
### Popup UI
|
|
- Quick bookmark add with optional notes
|
|
- Sync status indicator
|
|
- Settings button → opens config page
|
|
|
|
### Config Page (`background.html`)
|
|
- Server URL input (with validation)
|
|
- API key input (with "Get from Linkding Settings" guidance)
|
|
- Bundle tag input (e.g., "bundle_personal_firefox_1")
|
|
- Sync mode selector (`bi-directional` | `write-only` | `read-only`)
|
|
- Auto-generate tags toggle
|
|
- Last sync timestamp (human-readable with timezone)
|
|
- Test connection button
|
|
- Save Settings button
|
|
- Reset config button
|
|
|
|
### Background Service
|
|
- Monitor bookmark events (add, remove, modify)
|
|
- Perform sync operations with Linkding API
|
|
- Cache recent API responses
|
|
- Handle errors and show notifications
|
|
- Apply bundle tag to all new bookmarks
|
|
- Migrate old notes to structured format
|
|
|
|
---
|
|
|
|
## 6. Sync Logic Flow
|
|
|
|
```
|
|
1. Browser bookmark change detected
|
|
↓
|
|
2. Check syncMode:
|
|
- read-only: download from Linkding only
|
|
- write-only: sync to Linkding only
|
|
- bi-directional: sync both directions
|
|
↓
|
|
3. For new bookmarks:
|
|
- Add bundleTag to notes
|
|
- Check if URL already in Linkding
|
|
- If no → create new bookmark with bundle tag
|
|
- If yes + different URL → create new bookmark
|
|
- If yes + same URL → update notes (keep title/description)
|
|
↓
|
|
4. For updates (same URL, different folder):
|
|
- Update path in notes
|
|
- Merge with existing notes
|
|
- Apply sync mode rules
|
|
↓
|
|
5. For deletions:
|
|
- write-only → delete from Linkding
|
|
- read-only → ignore (keep Linkding version)
|
|
- bi-directional → delete from Linkding
|
|
↓
|
|
6. Migrate old notes:
|
|
- If notes not JSON or lacks version field → treat as old notes
|
|
- Convert to structured format with version 1.0
|
|
- Preserve existing content in userNotes
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Notes Migration
|
|
|
|
### Old Notes Detection
|
|
|
|
Old notes are detected if:
|
|
- `notes` is not valid JSON, OR
|
|
- `notes` doesn't contain a `version` field
|
|
|
|
### Migration Process
|
|
|
|
1. Parse existing notes
|
|
2. If invalid/non-JSON → create structured notes with:
|
|
```json
|
|
{
|
|
"version": 1.0,
|
|
"path": "",
|
|
"userNotes": "<existing text here>",
|
|
"autoTags": [],
|
|
"bundleTag": "<from config>"
|
|
}
|
|
```
|
|
3. If valid but old version → update version field
|
|
4. If same or newer version → keep as-is
|
|
|
|
### Forward Compatibility
|
|
|
|
When new fields are added to notes structure:
|
|
- Older extension versions ignore unknown fields
|
|
- Newer extension versions add new fields
|
|
- This allows gradual feature rollouts
|
|
|
|
---
|
|
|
|
## 8. API Endpoints Used
|
|
|
|
### Authentication
|
|
```
|
|
Authorization: Token <Token>
|
|
```
|
|
|
|
### Bookmarks
|
|
- `GET /api/bookmarks/` - List bookmarks
|
|
- `GET /api/bookmarks/check/?url=...` - Check if URL is already bookmarked
|
|
- `POST /api/bookmarks/` - Create bookmark
|
|
- `PUT/PATCH /api/bookmarks/<id>/` - Update bookmark
|
|
- `DELETE /api/bookmarks/<id>/` - Delete bookmark
|
|
|
|
---
|
|
|
|
## 9. Implementation Status
|
|
|
|
### Phase 1: Core Setup
|
|
- [x] Create manifest.json with options_ui
|
|
- [x] Create popup HTML/CSS/JS
|
|
- [x] Create background service worker
|
|
- [x] Implement storage helpers
|
|
|
|
### Phase 2: Sync Logic
|
|
- [x] Implement bookmark manipulation
|
|
- [x] Implement notes parser with versioning
|
|
- [x] Implement sync logic
|
|
- [x] Implement conflict resolution
|
|
|
|
### Phase 3: Configuration UI
|
|
- [x] Create config page
|
|
- [x] Implement config form logic
|
|
- [x] Add API key guidance
|
|
|
|
### Phase 4: Polish
|
|
- [x] Add icons
|
|
- [x] Add error handling
|
|
- [x] Add notifications
|
|
- [x] Implement notes migration
|
|
- [x] Implement tag-based bundle approach
|
|
|
|
---
|
|
|
|
## 10. Version History
|
|
|
|
| Version | Date | Changes |
|
|
|---------|------|---------|
|
|
| 0.1.0 | 2026-05-06 | Initial design document |
|
|
| 1.0.0 | 2026-05-06 | Initial implementation |
|
|
| 1.0.1 | TBD | Tag-based bundle support |
|
|
| 1.0.2 | TBD | Notes version migration |
|
|
|
|
---
|
|
|
|
## 11. Notes
|
|
|
|
This document should be referenced during implementation to ensure all requirements are met. Any deviations from this design should be documented here.
|
|
|
|
---
|
|
|
|
**Last Updated:** 2026-05-06
|
|
|
|
# LinkdingSync - Version Compatibility Notes
|
|
|
|
## Version 1.0.x - Basic Structured Notes
|
|
|
|
### Notes Structure
|
|
```json
|
|
{
|
|
"version": "1.0",
|
|
"path": "",
|
|
"userNotes": "",
|
|
"autoTags": [],
|
|
"bundleTag": "bundle_links_blabber1565_firefox_42",
|
|
"keyword": ""
|
|
}
|
|
```
|
|
|
|
### Auto-Generate Tags Feature
|
|
|
|
When `autoGenerateTags` is **enabled** in settings:
|
|
- Tags are automatically derived from the `path` folder structure
|
|
- Example: `path = "Work/Resources/Development"` → `autoTags = ["Work", "Resources", "Development"]`
|
|
- These tags are added to the bookmark on Linkding
|
|
- **Important**: Tags are only auto-generated if this setting is enabled
|
|
- Tags will always be present in the structured notes field
|
|
|
|
When `autoGenerateTags` is **disabled** (default):
|
|
- Tags array remains empty (`[]`)
|
|
- Only `bundleTag` is added to the bookmark
|
|
- Old bookmarks without structured notes are migrated without tags
|
|
|
|
### Bundle Tag Behavior
|
|
|
|
**Always Applied:**
|
|
- The `bundleTag` (e.g., `bundle_links_blabber1565_firefox_42`) is **always** added to new bookmarks
|
|
- This enables Linkding bundle filtering via the `all=` API parameter
|
|
- When viewing bookmarks in Linkding UI with this tag filter, you'll see only bookmarks from this browser
|
|
- Bundle feature works as expected: queries by tag filter, UI shows filtered results
|
|
|
|
**Not Applied:**
|
|
- `path` field is used for folder organization
|
|
- `userNotes` stores user-provided notes
|
|
- `autoTags` is populated only when `autoGenerateTags` is enabled
|
|
- `keyword` field is used for Firefox-style quick-access bookmarks
|
|
|
|
### Migrations
|
|
|
|
**Old Non-JSON Notes:**
|
|
```
|
|
"old text notes"
|
|
```
|
|
→ Migrated to:
|
|
```json
|
|
{
|
|
"version": "1.0",
|
|
"path": "",
|
|
"userNotes": "old text notes",
|
|
"autoTags": [],
|
|
"bundleTag": "bundle_...",
|
|
"keyword": ""
|
|
}
|
|
```
|
|
|
|
**Old Structured Notes (no version field):**
|
|
```json
|
|
{
|
|
"path": "",
|
|
"userNotes": "notes",
|
|
"autoTags": []
|
|
}
|
|
```
|
|
→ Migrated to:
|
|
```json
|
|
{
|
|
"version": "1.0",
|
|
"path": "",
|
|
"userNotes": "notes",
|
|
"autoTags": [],
|
|
"bundleTag": "bundle_...",
|
|
"keyword": ""
|
|
}
|
|
```
|
|
|
|
## Version 2.x - Future Features
|
|
|
|
Version 2.x will add new fields while maintaining backward compatibility. Older extension versions will continue to work but won't use new fields.
|
|
|
|
---
|
|
|
|
## Implementation Notes
|
|
|
|
### Current State (v1.0.x)
|
|
- `bundleTag` is **always** added to bookmarks (enables bundle filtering)
|
|
- `autoTags` is only populated when `autoGenerateTags` setting is enabled
|
|
- Old bookmarks get migrated with empty `autoTags` array
|
|
- Firefox tags (`autoTags`) are stored in Linkding bookmarks when present in Firefox
|
|
|
|
### Bundle Feature
|
|
- Linkding bundle is created with `all_tags` set to the `bundleTag`
|
|
- All bookmarks with this tag appear in the bundle
|
|
- Bundle filtering uses `all=` API parameter (exact match on all tags)
|
|
- Querying works correctly with pagination for large bookmark sets
|
|
|
|
### Cross-Browser Sync
|
|
- All browsers share the same Linkding collection via tag filtering
|
|
- Firefox bookmarks with tags sync to Linkding
|
|
- Other browsers read from Linkding with same tag filter
|
|
- Keywords are preserved in structured notes but not synced (Linkding doesn't support keywords)
|