A structured, ID-driven resource management system that replaces Chrome bookmarks with deduplication, redundancy, and broken-link recovery.
- Open
chrome://extensions - Enable Developer mode (top-right toggle)
- Click Load unpacked
- Select the
extension/folder
| Situation | What you see |
|---|---|
| Chrome internal page | "Cannot be tracked" message |
| URL already tracked | Resource card + quick tag/rating editor |
| URL's ID matches an existing resource but URL not yet linked | Warning indicator + resource preview + "Add this URL" action |
| Completely new URL | "Add Resource" form with auto-detected ID |
All states run tag detection immediately on open — detected tags appear as chips before the user confirms anything.
Search & Filter
- Full-text search by ID, title, URL, or tag alias (live, debounced)
- Tag filter with autocomplete — type to search all Tag Group aliases, select as chips (AND logic)
- Tag exclusion filter — same chip autocomplete, NOT logic
- Minimum rating filter
- Pattern-IDs-only toggle
- Sort by: last modified, created, rating, or ID
Resource management
- Edit ID, URLs, titles, tags, and rating inline via modal
- Merge two resources with full preview before committing
- Delete resources
- Open any resource in incognito (reuses existing incognito window if one is open)
Random unrated (🎲 Random button) — picks the oldest of 3 randomly selected unrated resources and opens it in incognito
Import (Chrome bookmarks)
- Displays bookmarks as an expandable tree with tri-state checkboxes
- User selects exactly which folders/bookmarks to import
- Folder names become tags (root and browser-level folders ignored)
- Preserves bookmark creation/modification dates
Migration Wizard — generate new URLs for ID-based resources from templates
- Define one or more URL templates using
{ID}as placeholder (e.g.https://example.com/browse/{ID}) - Preview generated URLs across all eligible resources
- Optionally validate a sample via HEAD requests
- Apply — adds new URLs non-destructively, existing URLs are never removed
Tag Groups — manage tag aliases, rename labels, merge groups
Bulk URL removal — remove all URLs matching one or more domains from every resource at once
Priority Domains (⚙ Settings) — ordered list of preferred domains used when selecting which URL to open; within the same domain tier the longest (most specific) URL is preferred; fallback also picks the longest URL overall
Backup / Restore — full JSON export (pbf-backup-YYYY-MM-DD.json) and import with merge or replace modes
Resource {
id: string // Normalized uppercase (e.g. ABC-123 or custom name)
isPatternId: boolean // true if id matches \w{2,8}-\d{3,5}
urls: string[] // Deduplicated set of normalized URLs (case-insensitive)
titles: string[] // Deduplicated page titles (auto-fetched + manual)
tags: string[] // Tag Group IDs — never raw strings
rating: 1|2|3|4|5|null
createdAt: number // ms timestamp
updatedAt: number // ms timestamp
}
Tags are not stored as raw strings. Each tag is a Tag Group:
TagGroup {
id: string // internal key, e.g. "tg_1700000000_abc12"
primaryLabel: string // label shown in UI
aliases: string[] // all equivalent labels (includes primaryLabel)
createdAt: number
updatedAt: number
}
Resources store Tag Group IDs in their tags array. When assigning a tag, all aliases are checked — an existing group is reused on match; otherwise a new group is created.
- Pattern:
\w{2,8}-\d{3,5}— prefix may contain letters, digits, or underscores - Examples:
ABC-123,380sqb-139,MY_PROJ-4567 - Case-insensitive:
AAA-123 = aaa-123, butAA-123 ≠ AAA-123 - A URL containing multiple IDs is treated as a manual-name resource
- Stored with original casing; compared and indexed case-insensitively
- Fragment (
#hash) stripped; trailing slash stripped from non-root paths - Duplicates are silently discarded on every write path
Adding a URL whose extracted ID matches an existing resource automatically links the URL to that resource without creating a duplicate.
When the popup opens on a tracked page the extension scans the page for structured label-value content (tables, definition lists, label+sibling patterns) and checks values against all Tag Group aliases. Matches surface as "detected" chips the user can confirm or remove before saving.
Rules:
- Only values in structured data are tested, never label text
- Matching is case-insensitive and word-boundary strict (no partial substrings)
- No new Tag Groups are ever created automatically
Generates new URLs for resources that have a pattern ID using URL templates.
Template syntax: any URL containing {ID}, e.g. https://archive.example.com/{ID}
Workflow:
- Enter one or more templates
- Preview — every (resource × template) pair, flagging already-present URLs
- Validate (optional) — HEAD-tests a random sample, shows success %
- Confirm — adds generated URLs non-destructively; existing URLs are untouched
- Resources without a pattern ID are skipped entirely
extension/
├── manifest.json
├── create_icons.py ← regenerate icons (requires Pillow)
├── icons/ ← PNG icons: 16, 48, 128 px
├── src/
│ ├── constants.js ID regex, storage keys (pbf_*), message types
│ ├── id-extractor.js ID detection, normalization, pattern test
│ ├── url-utils.js URL normalization, urlKey, domain helpers, priority sort
│ ├── rate-limiter.js Async rate-limiting queue (title fetching)
│ ├── resource-manager.js Core CRUD, search, merge, bulk import
│ ├── tag-group-manager.js Tag Group CRUD, alias matching, merge, detection
│ ├── import-manager.js Chrome bookmarks → resources (tree + tag extraction)
│ ├── migration-manager.js Template-based URL generation + validation
│ └── backup-manager.js JSON export/import
├── background/
│ └── service-worker.js Auto-title fetching, badge updates, message API
├── popup/
│ ├── popup.html
│ ├── popup.css
│ └── popup.js Chip-based tag input, all popup states
└── dashboard/
├── dashboard.html
├── dashboard.css
└── dashboard.js Full management UI
All data is stored locally via chrome.storage.local. Nothing is sent to any server.
| Key | Contents |
|---|---|
pbf_resources_v1 |
{ [id]: Resource } — all resources |
pbf_url_index_v1 |
{ [urlKey]: resourceId } — fast URL lookup (lowercase-keyed) |
pbf_tag_groups_v1 |
{ [id]: TagGroup } — all tag groups |
pbf_pending_tags_v1 |
{ [resourceId]: tagGroupId[] } — tags detected by service worker, awaiting user review |
pbf_settings_v1 |
{ priorityDomains, titleFetchEnabled, titleFetchDelay } |
On first run after upgrading from the previous version, all rim_* keys are automatically migrated to pbf_* and deleted.
| Permission | Reason |
|---|---|
storage + unlimitedStorage |
Store up to ~10 k resources locally |
tabs |
Read URL/title from active tab; update badge |
activeTab |
Popup access to current tab content |
bookmarks |
Read Chrome bookmark tree for import |
scripting |
Inject page-structure extractor for tag detection |
windows |
Open / reuse incognito windows |
<all_urls> |
Background title fetching and migration URL validation |
Designed for ~10 000 resources. All filtering and search run in-memory after a single load from chrome.storage.local. The dashboard paginates results (50 per page) to keep rendering fast.