Skip to content

Repository files navigation

vBookmarks

English Readme | 中文说明

Donate me | 捐赠

Image of vBookmarks

Available on WebStore · HomePage

vBookmarks turns your bookmark pile into a fast, keyboard-first workspace. One click on the toolbar icon opens a seven-view manager that lives in the popup (or Chrome's side panel — your choice): the familiar folder tree, instant fuzzy search, a tab-groups view, a staging workbench (with the recently-added timeline folded in), visit statistics, a dead-link scanner, and a duplicate cleaner. Everything is reachable from the keyboard, every delete is undoable, and nothing ever leaves your browser — no accounts, no telemetry, no build-step black box, just plain JavaScript you can read.

  • Seven views, one popup — Tree / Search / Tab groups / Staging (with the recent list inside) / Stats / Dead links / Duplicates, switched from an icon tab strip or with Alt+1…7.
  • A maintenance crew for your library — scan for dead links with pause & resume, deduplicate with six keep-strategies and undoable batch cleaning, save a whole window of tabs as a session folder.
  • Keyboard-first for real — every view is fully operable without a mouse: arrows, Enter, F2 rename, Delete, view shortcuts, and a Ctrl/Cmd+K command palette.
  • Fast and quiet — fzf-style fuzzy search with match highlighting (CJK-friendly), omnibox search (* + Space), and sync-status indicators that stay out of your way.
  • Make it yours — thirteen built-in themes (follow-system, light, dark, plus ten crafted looks: Ink, Paper, Classic (v3) light & dark, Dracula (@Andrew), Solarized Light, Monokai Pro, Dracula (the spec remake), Quiet Light, GitHub Light HC), importable theme packages, custom CSS, custom toolbar icon, per-view visibility toggles — or hide the tab strip entirely for the classic one-pane look.
  • Private by design — local-only data, 43 languages, MIT licensed, changelog and issues in the open.
  • Chrome and Edge — one MV3 package installs on both (npm run package builds dist/ and produces the store zip; python3 scripts/package.py --target edge for a direct source zip; Firefox would need a build step — evaluation in browser-compat.md).

Released under the MIT License. Read the FAQ. New in 4.0? Read the v4 feature guide.

Why vBookmarks

  • Modern, calm UI — a full design-token system with thirteen built-in themes: follow-system, light, dark, the two crafted "fable" themes Ink (deep dark) and Paper (warm light), the v3-memory Classic pair (light & dark), and six editor classics — Dracula (@Andrew), Solarized Light, Monokai Pro, Dracula (an original composition off the classic spec), Quiet Light and GitHub Light HC — plus user theme packages (.vbmtheme.json) with a token-level stable API — an 86-key vocabulary that spans the whole spectrum from quiet to showpiece: fill twelve colors and stop for a serene look (Solarized Light), or go glass-and-neon (Dracula (@Andrew)'s frosted menus and standing selection phosphor, Ink's instrument grid, Monokai Pro's workbench punch) — every channel a documented, package-validatable key.
  • Find anything instantly — fzf-style fuzzy search with match highlighting (CJK-friendly), persistent search history, and a command palette (Ctrl/Cmd+K) unifying bookmark search, folder jumps, view jumps and power commands.
  • Power tools built in — a dead-link scanner with dual-channel checks and pause/resume, a duplicate cleaner with six keeper strategies and undo-safe batch deletion, a session saver, and one-click undo for every delete.
  • Quick add everywhere — star button in the popup, Ctrl/Cmd+D, the global Alt+Shift+S shortcut, or "Bookmark this page" from the page context menu.
  • Sync-aware — on Chrome 138+ it understands Chrome's dual local/synced bookmark storage: local-only subtrees are gently dimmed, roots are labeled (Local) / (Synced), and cross-storage drags are blocked with a polite toast instead of a hard failure.
  • 43 languages, all aligned to the English baseline and kept in sync by an LLM-assisted translation pipeline.
  • Private by design — plain ES6+ JavaScript, no framework, no telemetry; development needs no build step (release packages are built from dist/ via npm run build). Visit statistics are stored locally, can be paused with one switch and erased with one button.

What's new in 4.0

4.0 is the largest release in the project's history. It rebuilds the popup around a view system — seven specialized views behind an icon tab strip — while keeping the classic tree experience one setting away.

The view system

  • Seven views: Tree (the classic), Search, Tab groups, Staging (the recent list lives inside), Stats, Dead links, Duplicates. The icon tab strip shows live count badges (dead marks, dupe groups, tracked pages) and each feature view (Tab groups/Staging/Stats/Dead links/Duplicates) can be hidden — or fully disabled — per view, or the whole strip can go for the classic single-pane layout.
  • One keyboard model everywhere — the tree's mature semantics (arrows, Home/End, PageUp/PageDown, Enter, F2, Delete, type-ahead) now work identically in every list view; ↑ past the first row steps up to the tab strip, then the search box; the strip itself is arrow/Home/End navigable with roving tabindex and RTL awareness; Alt+1…7 jumps straight to a view (portable across Chrome and Edge, which reserves Ctrl+1…8 for its own tabs).
  • Layered Esc — context menu → command palette → view-level action (e.g. pausing a scan) → clear search → back to tree → close, always peeling one layer at a time.
  • Popup vs panel — both reopen on the view you left (the popup via the default-on remember the last view switch — turn it off for the classic always-tree boot), and the side panel is ready to become your always-on bookmark workspace.

Search view — dual zone

  • The popup search is now a proper view: history on top, results below. Both stay on screen together.
  • Search history (MRU 10) records what you actually used — pressing Enter, opening a result, or leaving the view — with result counts and relative timestamps. Click or press Enter to re-run, Delete or right-click to remove one or clear all. A setting turns history off (and wipes it) instantly.
  • Leaving and coming back keeps everything: the box, the results, the scroll — no reflow, no re-query.

Tab groups view

  • Your browser session, one list: every window's tabs in real tab order — grouped tabs under their Chrome tab group (title, color, member count), ungrouped tabs as plain rows, with the current tab flagged. Collapse a group here and the browser's group collapses too (optional sync switch).
  • Full tab bookkeeping without leaving the popup: activate, pin/unpin, sleep/wake, close, drag to reorder — per tab or per group, from the row buttons, the context menus, or the keyboard.
  • Selection mode for batch work: group selected tabs into a new group, open them into an existing one, close or sleep them — and when a selection already belongs to a group, choose copy (reopen elsewhere) or move (leave the old group).
  • Bookmarks meet tabs: one click files a tab into your quick-add folder; a whole group saves as a bookmark folder and remembers its color and title, so open as tab group later restores it looking the same.
  • Recently closed groups live at the bottom (depth configurable 5–50): reopen the whole set, or restore/bookmark/forget individual tabs.

Staging / Recent view

  • The old in-tree "recently added" strip grew up into a staging workbench — a scratch desk for links you're not sure about yet (the full walkthrough lives in the v4 guide §3.3). Rows are dual-state: a solid ★ means the item is bookmarked, an outline ☆ means it's merely parked in staging. Build your own groups — drag rows between groups, drag group heads to reorder — and batch open / favorite / group / move / copy / delete / remove-from-staging with selection mode; destructive actions leave an undo toast.
  • Your newest bookmarks stay grouped into Today / This week / This month / Older, each row with a relative-time badge and a path · exact time second line.
  • R (or right-click) reveals any row in the tree; an optional one-time import from Chrome history (off by default, permission requested only if you opt in) back-fills older visits.

Stats view — your actual usage

  • Visit statistics, 100% local: rows you open from the popup are counted, and a background collector notices when you navigate to bookmarked URLs from anywhere else (deduplicated, so one open never counts twice).
  • Sort by count or recency (persisted), clear everything with one ConfirmDialog-gated button, or flip statsEnabled off — recording stops completely, and the erase exit stays reachable.
  • A Recently visited section lists what Chrome says you actually visit; bookmarked rows show a ★, everything else has a one-click ☆ to file it away.

Dead-link view

  • Dual-channel checking: a direct fetch first; on failure, a second channel gets the final say — so "down for everyone" and "just blocked here" read as different badges. The second channel is your own proxy server (http/https/socks5) added in one click from the strip above the list (reachability-tested before it is saved), or a legacy relay URL template from the options page.
  • Proxy mechanics, honestly scoped: checks through your proxy are routed by a temporary PAC script that matches only the scanner's own marker-tagged probe URLs — every other tab's traffic stays on its normal path, and the settings are restored the moment a scan settles, cancels or the popup closes. The proxy permission is declared at install time (Chrome does not allow it as an optional permission); it sits completely unused if you never configure a proxy server or never run a scan — no proxy code path executes otherwise. Known conflict: a few proxy/acceleration extensions (e.g. iGuge) actively disable any enabled extension that declares the proxy permission and is not on their whitelist — see the issue #53/#57 analysis for the verified root cause and what to do if you're affected.
  • Progressive, pausable scans that outlive the popup: the scan runs in the service worker, so closing the popup or side panel mid-scan loses nothing — reopen to a live mirror, and an interrupted run even resumes itself. Results stream in row by row; Esc pauses/resumes without losing progress; canceling restores the last completed snapshot. Progress ticks repaint silently, never moving your scroll position.
  • A risk banner before the first cleanup (dead links and dedup both rewrite bookmarks in bulk) links to Chrome's own backup & restore guide; Don't show again snoozes it until the next major or minor version (patch updates stay silent).
  • Tunable concurrency (1–16) and timeout (2–30 s), a dead/blocked/all filter with a dead·blocked summary line, and dead marks — flag a link once and the red ✕ follows it across the tree, search, recent and stats views.

Duplicates view

  • Finds real duplicates, not just string matches: URLs are normalized (tracking parameters like utm_*/fbclid/gclid stripped, hash dropped, optional http/https folding) before grouping.
  • Six keeper strategies — oldest, newest, bookmark-bar, shortest title, shallowest, most-visited (live data from the Stats view) — plus per-group manual pinning with K or the radio button.
  • Preview first, execute after: doomed rows render struck-through until you commit; batch cleaning runs through the undo chain with a single summary toast, and the last result set is snapshotted so reopening the popup paints instantly while it re-validates in the background.

Command palette, upgraded

  • Ctrl/Cmd+K in the popup, Ctrl/Cmd+Shift+K anywhere: fuzzy bookmark search, folder jumps, and a curated slash-command table — one Go command per view, /add /new /folder, /session, /theme <name> (or the direct switches /dark /light /ink /paper), /tabs and /options, each with a short alias or two.
  • Custom slash commands: open a URL, fill a URL template from the rest words (/g kimi code), open a bookmark folder as a tab group, or jump to a view with a preset — managed in the options page's Commands group, saved straight from the palette's Save as a command row, synced across devices, and editable/deletable from the row's → menu.
  • A plain query that isn't a command offers a bridge row to run it in the full search view; the palette closes itself when it loses focus.

Settings, backup & the classic look

  • The options page grew a Views group — tab-strip and per-view visibility, count badges, the remember-last-view switch, the palette/quick-add/tool-button chrome toggles, and a one-click Restore the classic header button — plus a Dead scan group (proxy template, concurrency, timeout).
  • Backup & restore: export every setting to a stamped JSON file, import it back with merge semantics — moving machines no longer means re-clicking forty toggles.
  • Options and advanced options are now one merged page with a responsive multi-column card layout that stays readable from 320 px to 4K (the old advanced-options URL redirects).
  • Classic-mode friendly: one click on Restore the classic header — or a hand-picked set of toggles — and 4.0 behaves like the vBookmarks you know; every new surface is opt-out.

The v4 foundation

  • Search field fixed and modernized: click-through dead zone removed, a proper clear button that always appears when text is present.
  • Adding a bookmark/folder/separator into a collapsed folder now works visibly: the folder expands and shows the new node immediately.
  • "Copy title and URL" moved to the async Clipboard API (clipboardWrite permission) — it works again.
  • Sync presentation reworked: synced rows stay quiet (no green-dot noise), tooltips localized, dual-storage roots labeled (Local)/(Synced), blocked drags show a toast, and "highlight unsynced" finally dims local-only subtrees.
  • Inline SVG icons throughout (folders, bookmarklets, twisties, view tabs); bitmap icons retired.

Engineering

  • Unit tests across 88 Vitest suites, covering every module — including contract tests that pin the row-alignment geometry, the z-index layering table, per-theme badge contrast and the horizontal-scrollbar protection contract (every scrollable pane clips overflow-x, text slots ellipsis, fixed slots flex: none, zoom rules never alter geometry). The live count is npm run test:run output (and the CI badge).
  • Docker harness: zero-console-error smoke, a real-browser keyboard/view verification suite (tab-strip keyboard model, focus zones, header-row arrow chain, per-view ↑↓/past-top crossings with the in-list toolbar rungs — the dead view stacks two, the tab-groups view three nesting levels, custom palette commands end-to-end, banner keyboard reachability, search dual-zone, per-view rendering — 168 hard assertions), a scrollbar matrix probe (screen resolution × browser zoom × in-extension zoom × popup size sweep, no horizontal scrollbar on any pane, the staging and tab-groups panes included — 903 assertions), and screenshot suites across 9 themes and 8 UI languages (with an RTL mirroring check).
  • Unified locale tooling (scripts/i18n.py): audit, missing-key reports, LLM batch translation, verify gate. Baseline grew from 75 to 345 keys at 4.0 (685 as of 4.1.0), all 43 locales aligned.
  • CI: GitHub Actions runs the unit suites, the i18n gates and the release packaging on every push and PR.
  • Repository organized for the v4 era: src/, pages/, css/, assets/, scripts/; obsolete artifacts (old release/*.crx, MV2 leftovers) live on in git history.

Feature highlights

  1. Seven views in one popup: tree, search, tab groups, recent, stats, dead-link scan, duplicate cleaner.
  2. Bookmark current tab before/after a selected bookmark or folder, or to the top/bottom of a folder.
  3. Add sub-folders, update a bookmark's URL with the current tab, copy title + URL to the clipboard.
  4. Search history with re-run, per-item remove and clear-all — can be disabled (and wiped) in settings.
  5. Visit statistics with a pause switch, one-click erase, and a recently-visited section powered by optional Chrome-history access.
  6. Folder content sorting: by title or date, folders-first option, optional recursion.
  7. Synchronizable bookmark separators with customizable style.
  8. Colored tags that cut across folders: context-menu tagging, batch tagging over selections, row chips with pin support and six display styles, a dedicated tag manager, and #-query search (autocomplete panel → member lists, co-tag rail, #! untagged filter). Tags sync across devices via bookmark guids.
  9. Dark theme done right: thirteen built-in themes on shared design tokens — follow-system / light / dark / ink / paper / classic (v3) light & dark / Dracula (@Andrew) / solarized-light / monokai-pro / Dracula (the remake) / quiet-light / github-light-hc — plus importable user theme packages.
  10. Optional side-panel mode (opt-in setting; popup stays the default), with an Alt+Shift+B shortcut.
  11. Command palette (Ctrl/Cmd+K, or Ctrl/Cmd+Shift+K globally) and omnibox search: type * + Space in the address bar.
  12. Full keyboard support in every view, and drag & drop rearranging in the tree.
  13. Sync-status awareness with quiet visuals: only local-only and unsyncable rows are marked.

Image of vBookmarks features

Notes for advanced features

  1. Omnibox search — type * in the address bar, press Space, then enter your keywords.
  2. Full keyboard support, identical across all seven views (details in the v4 feature guide):
    • ↑↓ move selection; ↑ past the first row steps up to the tab strip, then the search box
    • ←→ on the tab strip switch views; → on a row opens its context menu, ← closes it
    • Enter / Space to open; Ctrl/Cmd+Enter to open in a new tab
    • Home / End, PageUp / PageDown; Alt+1…7 jump to a view directly (Ctrl/Cmd+1…7 is the legacy twin where the browser allows it)
    • Delete to delete (undoable), F2 to rename, R to reveal in tree; K pins a keeper in Duplicates, M toggles a dead mark
    • Type-ahead filtering in the tree and search views: start typing to find items by name
  3. Middle-click a folder to open all its bookmarks (as a color-coded tab group).
  4. Ctrl+F focuses the search field; Esc clears the search, dismisses the context menu, pauses a scan, or closes the palette — layered from inner to outer.
  5. Command palette (Ctrl/Cmd+K inside the popup, Ctrl/Cmd+Shift+K globally):
    • Fuzzy-search bookmarks and folders, jump to a folder in the tree, or run slash-commands
    • Slash-commands: /tabgroups /recent /stats /dead /dupes jump to views, /session saves the window's tabs, /options opens settings, /theme <name> switches themes, /tabs toggles the strip — plus your own custom commands (options page → Commands group, or the palette's Save as a command row)
  6. Drag & drop to rearrange; dragging across synced/local storage is safely blocked with an explanation.
  7. Decide whether the popup closes after opening a bookmark (option in settings).
  8. Show only the Bookmark Bar (option in settings).
  9. Open bookmarks in background tabs (option in settings).
  10. Control the popup zoom level in settings.
  11. Options page: the Separators group customizes separator title/URL/style; the Dead scan group tunes scan concurrency/timeout.
  12. Options page: custom CSS for the whole popup (CodeMirror editor, Custom Styles group), e.g. * { font-family: Consolas; }.
  13. Options page: replace the toolbar icon with your own (Custom Icon group).
  14. Disable popup auto-resize to keep a fixed height.
  15. Export/import all settings as JSON from the Backup group at the bottom of the options page.

For developers

Dev needs no build step — Load unpacked the repo root in chrome://extensions/ for the dev form. The release form is dist/: npm run build, then Load unpacked dist/ (or npm run package for the store zip).

# Unit tests (Vitest — the live case count is `npm run test:run` output)
npm install
npm run test:run

# Headless harness (Docker)
scripts/harness/run.sh        # the verify gate: smoke + keyboard + scrollbars + menu verifies
scripts/screenshots/run.sh    # visual capture ONLY — suites into tmp/shots/
#   smoke.js               popup/panel/options raise zero console errors
#   verify-keyboard.js     tab-strip keyboard model, focus zones, view rendering
#   verify-scrollbars.js   screen×browser-zoom×in-extension-zoom sweep: no horizontal scrollbar
#   verify-menu-*.js       #48 menu overflow / collapsed flyouts / extreme zoom sweep
#   scripts/screenshots/shots.js         interaction states (light/dark)
#   scripts/screenshots/shots-matrix.js  9 themes × full surface (menus + flyouts + dialogs)
#   scripts/screenshots/shots-i18n.js    tree/tabs/menus/dialog/options × 8 UI languages
#   scripts/screenshots/shots-palette.js palette + the four feature views
#   scripts/screenshots/shots-guide.js   guide screenshots (search dual zone, options Views group)
#   scripts/screenshots/shots-tabgroups.js tab-group menus & dialogs, verified from the service worker
#   scripts/screenshots/shots-store.js   WebStore candidates: strip/promo×3/themes/options (7 views; pick 5)
#   scripts/console/       browser-console probes; scripts/harness/diag/ node probes (on demand)

# Locale pipeline (scripts/i18n.py, stdlib only)
python3 scripts/i18n.py audit      # keys used in code vs en baseline
python3 scripts/i18n.py missing    # per-locale missing / [TODO] report
python3 scripts/i18n.py translate --apply   # LLM batch translation
python3 scripts/i18n.py verify     # gate: alignment, TODOs, menu lengths
# translate reads the LLM endpoint from a git-ignored repo-root .env:
#   VBM_LLM_API_KEY=...  VBM_LLM_BASE_URL=...  VBM_LLM_MODEL=...
#   VBM_LLM_API_TYPE=openai|anthropic_messages

# Release build & store zip (version read from manifest.json; esbuild bundle + Terser into dist/)
npm run build                     # → dist/ (build-time self-checks included)
npm run package                   # build + zip → tmp/vBookmarks_<version>.zip
python3 scripts/package.py --root dist   # zip an existing dist/ (no rebuild)

tmp/ and .env are git-ignored. When adding runtime files, keep the include list in scripts/package.py in sync; see AGENTS.md for the full contributor guide.

Technical details

  • Powered by plain ES6+ JavaScript (no framework, no bundler); CodeMirror backs the Custom CSS editor.
  • Successor of Neat Bookmarks — thanks to cheeaun for the original work.
  • Old pre-4.0 releases (release/*.crx) were removed from the working tree and remain available in git history.

Changelogs

v4.2.3

2026-10-13

Fixed

  • Command-palette commands out of reach at any zoom above 100% (zoom clipping — the bottom commands and the close bar sat past the popup edge with nothing left to scroll): the palette scales with the extension zoom (Ctrl+wheel / Ctrl +/−), but its height budget mixed viewport units that paint multiplied-by-zoom inside the scaled panel — at 120% the last commands and the Esc bar were pushed out of the window while the list's internal scroll range sat below the fold, so rows like Version info / Switch language / Open options page were unreachable (every zoom level above 100% was affected, by however much). The palette is now sized in zoom-proof viewport bands: at every zoom level it ends inside the window, the close bar stays clickable, and the full command table scrolls internally — with the default-zoom proportions unchanged. The same overflow class is fixed in the standalone workbench (its full-window canvas and the floating details pane could paint past the window edge at high zoom). A new real-browser gate sweeps every zoom level and popup height (verify-palette-fit).

v4.2.2

2026-10-13

Fixed

  • Truncated toolbar labels in longer translations, with no hover fallback (#74 — most visible in Italian): the duplicates view's strategy/scope/order dropdowns, the stats view's sort buttons and the dead-link view's filter segments put full sentences into a toolbar that only has room for a few words, so in many languages they were cut off mid-word with nothing to hover for the full text. Every such control now carries the full wording as a hover tooltip (the same channel the icon buttons always had), and all 43 languages got compact center-word labels for these controls (e.g. "Più vecchi" instead of "Mantieni il più vecchio", "Dead" instead of "Dead only") — sized to the toolbar so nothing truncates in the first place; the full sentences remain on hover and for screen readers.

v4.2.1

2026-10-13

Fixed

  • The popup & workbench font regression (#73/#75 — for some users the whole UI fell back to Times/serif): tokenizing the body font in 4.2.0 kept the system-font keyword menu as the default value — CSS system-font keywords are only parsed inside the font shorthand; as a font-family value it is an unknown family name and the browser falls back to its standard font (Times New Roman on stock Windows). The default is now the supported system-ui, sans-serif (the OS UI face — the same face 4.1.3's font: menu resolved to). Instant workaround on 4.2.0: Options → Advanced → Custom CSS workbench, add :root { --vbm-font-ui: system-ui, sans-serif; }.

v4.2.0

2026-10-11

New

  • A brand-new tag system — grown into one workbench with the search view: a colored organization layer across folders. Tag or untag from the bookmark's context menu (the tag flyout lists every tag, uncapped), the hover tag-add button on search/staging rows, or a whole selection at once; the picker creates new tags in-place — type a name and the create row appears, auto-colored; the standalone inspector's tag section gives every chip a hovering × and zero-tag rows an always-visible add button. The nine tag colors are Chrome's own tab-group palette, constant across every theme.
  • # tag queries — the search view speaks tags natively: type # for the tag suggestion panel (autocomplete over names; exact match, click, or Enter completes); #a #b tokens intersect, -#a excludes, #! lists bookmarks with no real tags (pin-only included), and tags AND together with the field tokens (#work site:github.com is one query); active tokens ride the toolbar as removable chips; every result set carries a Narrow further: co-tag strip — click one to append another AND token, parked right under the count bar where you're already looking; a #tag member query's group head gains a mark all shown / unmark all pair of directional buttons over the whole result set; the search view's idle face IS the tag workbench — a hot-tag rail (top-8 recommendations, overflow gathered into a +N badge, an Add tag entry at its right end) above a foldable recent-searches list; a bare palette /tag lists your tags with counts, Enter runs the member query; the # panel's footer action bar (view all / new tag / manage) is arrow-reachable and Enter-activated — the whole panel stays pure-keyboard.
  • Tags on the row, six display styles: soft-tint pills (the default), solid pills, outlined pills, #-prefixed colored text, plain dots, or hidden — one ink formula across nine hues × every theme, WCAG AA held by a computed contract; overflow folds into a +n chip that expands in place (− or Esc collapses); row chips are click-to-query; a per-bookmark hide this tag on this row mask keeps individual rows quiet without touching the tag itself; a #tag member query force-shows the searched tag on its rows even where row-hidden.
  • Tag manager page: a dedicated master–detail page — the tag list (WYSIWYG pills that render exactly as rows do, live counts, per-tag show gates) drives a member panel (bulk re-assignment, open/copy, per-member hide switches, following external changes live). Rename with merge-on-conflict, recolor, folder pours, Raindrop #tag interop import/export, and a full reset. Deleting a tag — from the manager page, the picker, or the context menu's Delete tag (all bookmarks) — snapshots the assignment map before writing, and undo replays it exactly; all four delete paths share the one safety net.
  • Save-time tagging + chained creation: after the quick-add star (or Ctrl+D) saves a page, the confirmation toast gains an Add tags… action that opens the tag picker for the fresh bookmark — organize at save time, zero second-pass cleanup; no click and the flow is byte-identical to before. The new-tag dialog (whatever entry opened it) gains a Save & continue button — the tag (name + chosen color) commits, the name clears, the color stays selected and the dialog stays put for the next one; a rejection (duplicate name / the 100-tag cap) keeps the typed name in place with the inline reason.
  • Cross-device by design: tags ride bookmark guids and sync through chrome.storage.sync; on Chrome builds without per-node guids (< 138) tags stay strictly local, and a promotion path carries them into the sync tier the moment the browser upgrades — no silent loss, no cross-device misattribution. A one-time notice appears if a write ever exceeds the storage quota (the local mirror keeps working).
  • A real theme system, worn over everything: thirteen built-in themes on one color-temperature token chain — follow-system / light / dark / ink / paper / classic (v3) light & dark / Dracula (@Andrew) / solarized-light / monokai-pro / Dracula (the remake) / quiet-light / github-light-hc — with a theme manager that imports theme packages from CSS files (validated against the token vocabulary, with an AA contrast pre-check), a density setting (compact/comfortable/spacious rows) that stays orthogonal to skins, and theme styles living in a Custom CSS workbench instead of a buried textarea. Folder icons take on each personality theme's hue. The theme group's link rows pair the authoring guide with an official sample-themes download (the docs/samples directory on GitHub — grab a package, import it via the manager). Tag chips and the tag manager page are born speaking this visual language and follow your theme.
  • The standalone window grows into a real desktop workbench: a labeled left nav rail replaces the top tab strip (chrome://bookmarks / Raindrop layout language, RTL-mirroring), the header gains New bookmark / New folder buttons aimed at the folder you're in, and the window remembers its own view independently of the popup and side panel. On wide windows a second column pins the staging view permanently on the right — browse any view on the left and drag a tree row straight into it, or use the existing selection toolbar's Send to staging; the pane updates live for bulk filing, and switching the left pane to Staging hands over to a central Staging & recent mirror instead of duplicating the column. Narrow windows keep the familiar single-pane layout. The canvas width has three tiers (up to 1480px on very wide screens), the staging column drags to resize (280–520px) or collapses to a slim stub, the tab title follows the current view, and left-clicking a bookmark always opens a new foreground tab — the workbench never closes itself.
  • Drag & drop across windows: tree rows now drag natively across window borders — popup, side panel and standalone workbench inter-drag to move bookmarks; links dragged in from web pages or the address bar land in the tree or straight into staging (http/https only), carrying their titles across windows; the tab-groups view moves tabs across windows, and whole groups drag by their heads (onto a group head = its head position, onto a row = that row, onto a window head = window end; dropped into staging they file into a named group).
  • Workbench preferences in the options page: a new Standalone canvas group — default view, wide canvas, dual pane, plus a direct page link; tag preferences now sync live between the manager page and the options page; the palette's /open gains a standalone alias, and custom commands gain an open standalone action (view × tab/background/window placement).
  • View-tab tucking becomes an option: tucking the three cleanup views (stats / dead links / dupes) behind a ⋯ overflow menu — with the danger counts aggregated onto it — moves from default behavior to an opt-in. Every view keeps its tab by default, degrading to icons when narrow; the standalone workbench's full labeled rail never tucks.
  • The stats view gains a dwell-time dimension: per-URL foreground attention time is journaled by the service worker and survives browser restarts; sort by dwell, a dwell pill on each row, and visits / dwell time each clearable on their own.
  • Copy-selected on the batch toolbars: the search, staging, stats and dead-links selection bars all gain a one-click copy of the selected rows (title + URL lines), placed left of delete so the danger right-end law holds.
  • The dupes view sorts its groups: a new toolbar dropdown orders duplicate groups by their earliest member's added time (oldest → newest by default) — the group you bookmarked first surfaces first.
  • The recent-searches area folds as one: a single button left of Clear all collapses and expands the whole list, remembered per device.

Fixed

  • A restored #tag query no longer reopens the tag suggestion panel at popup startup — the panel used to cover the view tabs and the first tab click completed the token instead of switching views.
  • The tag dialogs now join the keyboard layer cake: Escape closes the tag picker / tag create-rename dialog (and the staging group-assign dialog) instead of falling through to lower layers — at worst three presses closed the whole popup with the picker still open; Ctrl+F no longer yanks focus out of a half-typed tag name; Tab cycles inside the dialog. Ctrl+D no longer adds or removes a bookmark behind an open dialog.
  • The standalone window carries the full tag UI: the standalone view's row tag buttons, the # create flow and the context-menu tagging entries used to silently do nothing (the tag dialogs and menus were missing from that page's markup); they now work identically to the popup.
  • Three Esc-layer and shortcut-guard fixes: an expanded inline-tag block no longer eats Escape ahead of dialogs, menus, banners and the palette; Alt/Ctrl+number no longer switches views behind an open dialog; the staging onboarding banner's Escape is no longer dead code that could never run.
  • Pure-keyboard completion in the tag suggestion panel no longer stalls: completing a token left the caret at the start of the value, so the search box's ↓ "caret at end" gate never fired; it now lands at the end.
  • Three native popovers on the tag manager page close on focus loss: the recolor / merge / add popovers used to ignore clicks on empty page space; outside mousedown and Escape now collapse them.
  • Standalone workbench keyboard gaps closed: the large-screen Recently added central mirror never collapsed (the class flipped without hiding); the header's New bookmark / New folder buttons were unreachable in the Tab ring; the nav rail's arrow keys rotate to the vertical rail on the standalone page — ↑↓ walk the rail, → enters the content, ← retreats (the narrow-surface horizontal law unchanged).
  • Tab-group header drops no longer died mid-sort in-view: one payload class slipped past the dragover gate, so a dragged group's drop never fired; the gate is closed with a contract test.
  • No more clipped placeholder tails in the 360px side panel: Chromium lacks ::placeholder ellipsis (Firefox-only), so overlong hints now fall back by measured width, re-chosen through a ResizeObserver as the panel resizes.
  • The R15 pre-release deep-audit round (four parallel audits + real-browser screenshot review; checklist in docs/plan-4.2.0/r15-release-polish.md): the Home view's bookmark rows were dead to clicks/Enter/middle-click — they now share every other view's row interactions; ?view= deep links can no longer be stolen by the remembered view (palette /open and direct links land correctly); the inspector's copy link no longer shows a blank toast and send to staging no longer shows a garbled one; the invisible drag strip on the workbench's left edge is retired (a stray drag used to collapse the layout into a narrow column) and the page no longer carries a 24px dead scroll; Ctrl+D on the workbench no longer bookmarks the workbench itself; narrow-window rows now size their anatomy by column width, not page width; the three newer header buttons join the arrow-key walk.
  • Tag interaction consistency: clicking a chip behaves identically in every view (the # suggestion panel no longer reopens over the results); the tag manager's member list follows external changes live (including sync from another device); its batch bar gains the missing stop showing selected twin button and a selected count; the tagging flyout lists each tag's color dot; creating a tag from an empty library now carries the name you just typed instead of dropping it.
  • Theme-system robustness: re-importing a theme package with the same id updates it in place and applies immediately on every open page; switching themes across devices no longer leaves two themes' CSS stacked; theme entries arriving via backup import go through the same validation as file imports (closing a selector-injection hole); a custom theme no longer flashes its base theme on first paint; the missing-theme notice is styled and self-dismissing on every page; theme packages gain a total budget (20 / 2MB) and storage write failures are no longer silent.
  • Built-in theme visual corrections: Classic's floating-layer shadows lose their light-theme blue tint (back to neutral); Paper's nav rail on the standalone page is transparent again and its primary-button hover passes contrast; Solarized Light's hover passes contrast; CodeMirror's dark scheme now covers Classic Dark / Monokai Pro / the Dracula remake (cursor and selection were unreadable); short pages like the tag manager no longer show a white band below the content on dark themes; selected rows re-tint their metadata to the selection foreground (fixing the library-worst 1.85:1); warning/success/danger gain text-level semantic colors, held to AA by the computed contract across every theme.
  • The R17 pre-release round (checklist in docs/plan-4.2.0/r17-release-fixes.md): performance, profiled against the maintainer's real 5,000-bookmark data with the existing perf harness — the row hover-action tail is 46% of a row's HTML (A/B: dropping it saves 26% of the load), i18n labels are now memoized per render burst (~25k cross-process calls removed on a fully expanded tree), and the boot's extension-API traffic dropped from 5 calls / 1428 ms to 3 / 585 ms (no more per-key popup-size reads, no more whole-area favicon read); the popup no longer paints narrow and then grows into the remembered width — the size lands before the first paint. Bookmark identity moved to guid/id everywhere the URL still acted as one: two distinct bookmarks sharing a URL are now two staging rows, the reported “remove one staged row, both vanish” chain (intra-batch duplicates + URL-sweeping removal) is fixed at the root, and the stats view no longer deletes BOTH real bookmarks when one of two duplicate-URL rows is selected. Classic theme: on the dark blue right-click highlight, the soft tag chips go from 1.03–1.88:1 to 4.70–8.19:1 (new --vbm-row-active-chip-base channel, zero-change default). Paper theme: the desk is back at 4.1.3 lightness (L*95.6), the two weave layers are gone (calmer grain), the menu hover is a warm paper wash instead of gray (R−B=22), and --vbm-muted's two AA shortfalls are fixed. Copy/move folder picker: a “New folder…” entry — the existing dialog chained on top of the picker (Esc peels one layer), creating under the selected folder and re-selecting it on return. Wide screen: collapsing the right pane now takes the inspector with it (it used to overflow the 28px stub column and cover the widened main list), and the [Staging|Details] switch stands down. Extremely narrow rows: a tree row with no always-on state glyph lets its title own the full width (no more 72px reserved for three hover buttons); hovering/focusing truncates and reveals them, while rows with a persistent glyph keep the old geometry.
  • The R16 tag-row round (checklist in docs/plan-4.2.0/r16-tag-row-fixes.md): the +n expansion no longer orphans to the bottom line — it takes over the tag row itself (the middle line of the title/tags/meta order), listing every chip with its own − toggle instead of printing the same chips twice; the tab-groups view's row chips had no wiring at all, so clicking a tag fell through to activating that browser tab — it now runs the #tag member query like the other six lists, +n expands in place, and a middle-click no longer opens the tab either; the search view's “Narrow further:” strip moved from under the last result row to directly beneath the result count, above the rows, with the keyboard rung order following the visual one.
  • A folder opened from the search results lands at the top of the tree again (issue #71, regression since 4.0): clicking a folder result opened and expanded the folder but left the viewport wherever it was, because the reveal's scroll was aimed by the shared "last focused row" memory — and the re-render's own focus park/restore re-memorized the tree's first row in between, so the reveal scrolled to that row instead. The reveal now carries its own target through the render and always lands on (and highlights) the folder you picked, whatever the memory says.
  • A new search starts at the top of the result list (issue #71): the results pane is a persistent element, so a second query used to render at the previous query's scroll position. Turning the "remember scroll position" option off never helped — the search view is not one of the remembered ones. Re-renders of the same query (the path-label heal, a selection-mode repaint) still keep your position.
  • Scroll memory, three more roots closed: locating a bookmark in the tree can no longer be dragged back by the activated scroll memory; the debounced save can no longer wash away the just-restored position stamp; and a permanently-false pagehide guard no longer skips the final save on close.
  • Staging is guid-keyed end to end: the fuzzy-rank projection that silently dropped guids (the root of several identity bugs) is fixed, shortcut entries carry folder guids so dead folder ids heal by guid, and pre-guid staged entries migrate across the key boundary in both directions.
  • The tag manager's drag and keyboard reordering work: two defects fixed, nine regression tests added.
  • Row chips shed atomically and counts keep their shape: an overflowing tag chip leaves the row whole before any single chip truncates its own text, and the co-tag strip's count splits into a non-shrinking counter — the name ellipsizes progressively, the count never deforms.
  • In-row folder paths follow moves instantly in the recent, staging and tab-groups views and the inspector.
  • Context menus in the tab-groups and stats views are two-state complete: bookmarked tab rows gain the bookmark item set, unbookmarked rows gain copy/incognito, closed-tab state flips correctly, and staging pseudo-id items stay hidden.
  • Batch mark/unmark flips the in-row display button in sync — after Show all, the button no longer stays hollow.
  • The language picker lists self-names from a static table — Macedonian no longer renders as Chinese under a Chinese UI — and the palette's /lang list rides the same table.
  • The post-merge consistency round (2026-10-04; checklist in docs/plan-4.2.0/post-merge-polish-batch.md): the standalone Home view's empty state now wears the shared muted, centered treatment — it rendered unstyled, unlike the six list views; and the staging pane's fold chevron finally mirrors in RTL — the rule targeted body[dir="rtl"], an attribute nothing ever sets, so the mirroring had never actually applied (now on the live .rtl channel).

Polish

  • A three-stage state-glyph law across the toolbars: hollow = action, filled = state (click to undo), filled + bottom-right × = batch cancel — batch toolbars always carry a pair of independent static buttons instead of one flipping its own label; the × badge family switches to a cut-out contrast disc (a solid disc in the theme's background color) so the × reads against any glyph on any theme; the tag-add and mark-display glyph families were redrawn.
  • Theme visual polish: one-shot state-flip motion (star pop, mark light-up), a local Exo 2 font stack for the Dracula theme, radius on section-head focus backdrops and RTL closure for toolbar glyphs.
  • Each editor-class theme finds its own hand (all palette hexes re-verified against the official Solarized, Dracula and Monokai Pro specs): the Dracula (@Andrew) theme restores its author Andrew Neonlinx's cyan-sheen selection gradient at AA-tuned stops with the foreground glyph, and floating panels (menus, dialogs, the palette) pick up a whisper purple ambient halo; his original file remains the theme's advanced-CSS form of record. With thanks to Andrew (@Neonlinx), whose earlier hand-crafted Dracula theme for vBookmarks (the v4.1.3 Refined stylesheet) has now become part of the official set as Dracula (@Andrew). Under the new theme system, a Dracula theme natively designed against the Dracula spec is also on offer. The remake stays spec-literal — the flat official Selection, no hover pop, and a purple focus ring per the spec's own "Purple = focus indicators" mapping. Monokai Pro gets the workbench punch: a fuller hover pop, a focus glow that burns brighter than hover, and a yellow underglow beneath floating panels. Solarized Light hovers with the palette's own Base2 highlight tone (base tones run the UI states, blue stays selection's alone), halves its shadows, and drafts a 2px official-blue title rule across the view card.
  • The hot-tag rail closes up: the title drops its icon, the nowrap specificity root cause is fixed, and the rail caps at top-8 — the overflow gathers into a +N count badge that expands in place (Esc collapses).
  • The tag-add glyph reads "new" again: the 2026-09-06 large-aperture outline retires for the engraved disc + plus badge recipe (same family as the × badges), synchronized across every consuming surface.
  • The R27–R31 big-screen and narrow-tier layout rounds (checklists under docs/plan-4.2.0/): the central view gains its own scroll container and a two-column anatomy above 800px; cards are redesigned; the five view toolbars step down below 400px; zoom-256 windows get an all-rung single-row mode with widened cards; single-line rows unify column order with true vertical alignment; and row metadata splits into path + time columns with per-item exit animations.
  • Tag dialogs join the dialog input recipe: both tag dialogs' text fields — and the manager page's four — take the shared focus-ring styling, and the picker closes to full width.
  • One scrollbar everywhere (post-merge, 2026-10-04): the options, tag manager, Custom CSS and favicon gallery pages now use the same thin 8px theme-tinted scrollbar as the popup instead of the OS-native wide bar.
  • The default theme's keyboard focus glows too (post-merge, 2026-10-04): light was the only one of thirteen themes with no focus halo — focused rows now get the same 1px accent ring the other themes speak (classic/paper/solarized-light/quiet-light keep their whisper-none). Plus a first high-contrast safety net: under Windows forced-colors, keyboard focus and the selected row fall back to system Highlight outlines and floating layers regain a real border; prefers-contrast: more deepens the focus ring. (The full per-theme high-contrast review stays on the deferred ledger.)

Engineering

  • The tags merge into velvet is audited end-to-end: velvet-feat-tags.md checklist closed (theme links on the manager page, single-source nine-color palette, chip AA contract tests × 180, stable-selector registration, keyboard-model layers), plus real-browser probes (tags audit/usability/display/menu all clean, full harness 4/4, dist smoke). A final release-polish round audited the keyboard layer, page DOM parity and the save flow user-perspective (seven rounds total on record in docs/plan-velvet/), gated by a standing probe (diag-final-polish, 12 assertions).
  • Nine rounds of live-browser feedback batches landed and verified item-by-item through the release window (docs/plan-velvet/feedback-2026-09-06-batch.md and the per-batch records): cross-window drag & drop (CW1–CW6), options/palette parity (OP1–OP8), toolbar audit (TB1–TB7), Esc fixes (KE1–KE3) and three standalone polish rounds (r3), each with its own checklist and contract tests; every batch passed vitest (5,300+ cases), ESLint, i18n verify and the Docker real-browser gates (keyboard 175 assertions / smoke / menu / scrollbars). A bilingual docs/guide-4.2.md supplement guide (six new-interaction sections + eight visual-language sections) ships alongside, and the keyboard-model doc gains the layout-following laws (§2.3b/§5/§9).
  • Two i18n audit rounds: 113 translations placed across 32 locales, then 259 values across 38 (Slovenian staging terminology unified, Hungarian honorifics, Latvian tag declensions, theme brand names held equal to English in all 42 locales, and a full Persian orthography pass — ZWNJ across 140 keys); the menu-length warning baseline drops to zero after the two dialog-body keys moved out of the menu budget (a misclassification fix, not a copy change).
  • Test-audit round 2 and payoff: the gap whitelist cleared (+127 cases → 132 suites / 5,329 green), with one real product bug (the pagehide save guard) and one keyboard-gate drift fixed in flight.
  • The release-final freeze batch: the last hand-copied × single-sources to CLOSE_ICON, the standalone megaphone's stroke aligns to 1.5, the what's-new copy syncs to the shipped facts (thirteen built-in themes) across all 43 locales, and legacy markup debris is removed.
  • The post-merge polish batch (2026-10-04, checklist in docs/plan-4.2.0/post-merge-polish-batch.md): the documentation pages join the type scale (59 literal font sizes → --vbm-text-*, value-identical, zero visual change); the triplicated header icons and the announce megaphone gain cross-page drift guards pinned against the real renderer's output; and the z-index contract now covers the view-tabs overflow dropdown (Layer 5b, 310) and the standalone local layers (3/4/6) — every value in neat.css is asserted against the header table by the layering suite (132 suites / 5,371 cases green).
  • Motion literals closed, RTL verified (post-merge, 2026-10-04): the last two unexempted hard-coded transitions (the staging-star color fade, the sync-tooltip fade) ride the --vbm-dur-*/--vbm-ease tokens and the doc-page toasts carry their exemption pointers — every remaining literal now traces to a documented exemption; the doc pages' RTL sweep verified all eight physical properties correct as-is (editor internals stay LTR by domain, centering tricks are direction-agnostic), zero changes needed.

v4.1.3

2026-09-05

New

  • The huge-bookmark-tree speedup now lives in Labs (off by default): the tree-row rendering acceleration introduced in v4.1.0 was the upstream cause of this release's scroll-position troubles (its estimated placeholder heights and lazy off-screen layout are exactly what the fix chain below had to outgun). Most users do not have thousands of bookmarks, so the default path returns to the classic fully-laid-out tree with a one-shot, exact position restore — no rescue machinery, nothing to go wrong. If your tree is genuinely huge, enable Huge-bookmark-tree speedup in the Labs section of the options page; the position restore there lands on the remembered row and may run a brief top-down scan while the tree finishes laying out.

Fixed

  • Deep scroll positions survive slow machines (#66–#68): with the highlight off and the tree scrolled a couple of pages down, the popup could reopen at the wrong spot — the restore silently deadlocked against the tree's own layout laziness (off-screen rows only lay out once the viewport passes over them), so the stored position never became reachable and the restore gave up. The restore now detects the stall and walks the tree band by band from the top — forcing each band to lay out — until the exact position lands. Two field reports on the same build sharpened the picture further. First, for long folders with the highlight on: the walk could land mid-settle and starve on the retry budget (giving up partway), and Chromium's scroll anchoring could then drag the landed view away as late content growth kept arriving. The walk now runs on its own budget scaled to the depth, scroll anchoring is suppressed for exactly the duration of the restore campaign, and the campaign only ends after the geometry has been stable for several checks — the anchoring resumes protecting a final view instead of hijacking a settling one. Second, the position itself is now remembered by row, not by pixel: late correction waves trigger content-stable anchoring compensation — the view looks identical while the underlying pixel quietly changes — so a pixel memory random-walked across sessions and every reopen landed somewhere slightly different. The remembered row at the top of the viewport is persisted alongside the pixel and the restore lands on the row, immune to per-session layout differences. The restore's own give-up can no longer corrupt the saved position either (its trailing scroll event used to overwrite it).
  • The highlight no longer drags the reopened view around (#66, #67): with "Highlight the last opened bookmark" on, reopening the popup after scrolling deep could shift the view toward the highlighted row. The cause was Chromium's scroll anchoring preferring the focused row as its anchor during the post-render layout settle; the highlight's focus grant now waits until the restored position has landed, so the anchoring protects the view instead of hijacking it.
  • The saved position can no longer be corrupted mid-restore (#66, #68): on slower machines with big trees, the restore's own climb intermediates could be written back over the saved position (a popup closed mid-climb then "forgot" where you really were), and the retry budget was too short for slow settles. The retry now runs a ~4.5s campaign that never persists its own intermediates — your real scrolls take over instantly — and ends quietly at the best reachable position if the tree can never hold the target.

Engineering

  • The lazy-layout deadlock is reproduced deterministically by real-popup probes: diag-68-slow-settle-duel.js (a freeze-style slow-settle simulation — stuck at the clamp frontier before the walk, exact landing after) and diag-413-highlight-drift.js (the maintainer's live repro — long folder, highlight on, wrapped titles, plus a modeled headed-machine placeholder phase: landed-then-anchored-away to 57% past the target before the fix, pixel- and content-exact after). The rounds are archived under docs/issues/issues-65-66-memory-scroll.md.

v4.1.2

2026-09-02

Fixed

  • The tree reopens exactly where you left it (#65, #66): the scroll position was being saved correctly all along, but two bugs could throw it away when the popup reopened. The restore ran before the freshly rendered tree had finished laying out, so the stored position was silently clamped back to the top — with or without the highlight enabled, and even when you had only scrolled without opening anything. The restore now retries until the tree can actually hold it.
  • The last-bookmark highlight no longer shifts your position (#65, #66): the highlight restore — and the hidden focus-row memory that outlives it — used to scroll the view back to the row it remembered, overriding (and eventually overwriting) the saved position after you had scrolled elsewhere, e.g. after visiting a bookmark's page. Highlight and focus restores now never move the scroll position at all; the race between the two bugs is also why the position "often" came back either shifted or reset to the top.
  • Stale release announcements no longer queue up on the newest version: the v4.1.0/v4.1.1 what's-new banners in the remote announcement feed declared open-ended version ranges that never expire, so a client on the latest release was drip-fed each past release's news banner one popup open at a time (after the donation card's deferral). Both banners are now bounded to their own minor era, and a live-file contract test pins the rule for future releases.

Engineering

  • A real-browser regression gate covers all four repro shapes (highlight on/off, after the 4s highlight cleanup, and the click-a-bookmark-then-return flow) plus unit contracts for the clamp rescue and the preventScroll focus law; the two issue reports are archived under docs/issues/issues-65-66-memory-scroll.md.

v4.1.1

2026-08-30

New

  • A layered Memory options group (#65): "remember previous state" is no longer all-or-nothing. A master Remember previous state switch now heads a dedicated Memory group with four independent sub-toggles — Highlight the last opened bookmark (the most-requested off-switch, on by default), Remember the scroll position, Remember opened folders, and Remember the last search — while Remember the last active view keeps its own semantics beside them. Defaults change nothing; turning one layer off leaves the others intact, in the tree and the list views alike.
  • A multi-style Custom CSS workbench: custom CSS moved out of the cramped options textarea into its own editor page — one tab per style with its own name, description and enable switch, ◀/▶ buttons that reorder the cascade (later styles override earlier ones), and a full-width editor that scrolls long stylesheets internally. The concatenated enabled styles keep feeding the very same apply pipeline, and the old single-stylesheet setting migrates in as one entry automatically.
  • Full-info tooltips in every view (#62, #64): hovering a row — tree bookmarks and folders, search results, tab-group tabs, staging/recent, stats, dead links, duplicates — always tells the whole story now: title, URL, the labeled containing path and the date the bookmark was added, one fact per line. The tree's old "only when the title is truncated" adaptive tooltip is retired; the full block is simply always there.
  • Three new preferences: Focus the search field when the popup opens (Search group, off by default — start typing the moment it opens; the remembered-row focus restore stands down), Show folder rows in search results (Search group, on by default — turning it off drops folders before the 100-result cap so they can't spend it), and Show row paths with the nearest folder first (Views group, off by default) — row path labels flip to nearest-parent-first with a three-level depth cap (Frontend < Dev < …), while tooltips always keep the canonical root-first form.

Performance

  • The tab-groups view no longer flickers on sites that animate their tab title (glyph-flipping "live" titles): a title flip used to rebuild the whole list on a 300 ms debounce; now exactly the one affected row is patched in place (hover, focus and drag marks survive), and refreshes whose data changes nothing rendered — favicon or audible-notification churn included — paint nothing at all.

Fixed

  • The tree remembers where you were — again, in both places (#63): a popup closed mid-scroll no longer loses its final position (the last write was debounced and could die with the popup — a v4.0 storage-migration regression; a synchronous local shadow restores the old guarantee), and switching away from the tree and back no longer snaps to the top (a hidden container silently zeroes its scroll offset, and the first scroll after that overwrote the remembered deep position with a near-top value).
  • Reopened searches show their paths (#64): restoring a saved query on popup open used to render results with bare titles — the search painted before the shared path map existed; the results heal themselves the moment the map lands.
  • The last-bookmark highlight no longer fades away intermittently: opening a bookmark and closing the popup could kill the final "where I was" write (the debounced store write raced the popup teardown, the same close-path class as the scroll fix above) — the highlight now survives every close via a synchronous shadow, and the same guarantee covers the remembered row in list views.
  • Staging selection mode on wide popups and the side panel: the dashed group connector's horizontal tick painted ~27px to the right of its trunk, floating inside the title text (a wide-grid-only double offset), and the group-head glyph well sat half a pixel off the trunk axis — both re-anchored to the icon-axis law.
  • The Custom CSS editor geometry: a long stylesheet now scrolls inside the editor instead of growing past it and overlapping the page footer, and the editor follows the browser window width (a leftover 560px ceiling from the retired inline editor used to pin it).

Polish

  • Staging selection mode, re-anchored: the small group heads drop their fold chevron while selecting (folds are disabled there — it was dead chrome), and the dashed hierarchy lines now descend from the head icon's center axis through the clear gutter beside member favicons — the same icon-axis law as normal mode, no longer cutting through icon ink.

Engineering

  • The memory switches are pinned by a combination test matrix (master × every sub-layer × the view switch, both restore polarities, unit and real-browser E2E), and six locale translations of the new strings were corrected against their baselines; pre-4.0 release notes moved from the README into per-version files under docs/changelog/.

v4.1.0

2026-08-28

New

  • Tab groups view — the seventh view (between Search and Recent): every window's tabs in real order, grouped tabs nested under their Chrome tab group with title/color/member count, ungrouped tabs as plain rows, the current tab flagged. Group heads fold like tree folders (arrow-key model included), with an optional collapse-sync switch that mirrors the fold into the browser itself.
  • Tab and group management in place: activate, pin/unpin, sleep/wake, close, drag-to-reorder across groups and windows; group actions cover rename, activate, close, sleep, ungroup, move to a new window, and save as bookmark folder (group color/title are remembered and restored by open as tab group). Four dedicated context menus (tab row, group head, closed group, closed tab) join the keyboard menu model.
  • Selection mode with copy/move semantics: batch group-into-new-group, open-into-existing-group, close or sleep; selected tabs already in a group prompt a copy-or-move choice. Selected tabs can be bookmarked into a chosen folder via the new folder-picker dialog.
  • Recently closed groups: the last N closed groups (configurable 5–50) stay listed with per-tab restore/bookmark/remove and a reopen-all, persisted like search history.
  • Options: the tab strip gets the Tab groups show/disable controls like every feature view; the new Tab groups options group carries the group-color style (off / edge band / connector line) and the closed-history depth. The palette grows /tabgroups, and the what's-new banner mechanism re-arms once for this release.
  • Staging area workbench — the Recent tab grew up: the seventh-tab slot is now a staging workspace whose rows are dual-state — bookmarked items wear their ★, unbookmarked/history items sit beside them with an outline ☆, so "already saved" and "just parked here" read at a glance. You build your own groups: drag rows between groups, drag group heads to reorder, and group-head menus cover select-all-in-group, save-to-folder, copy the list, dissolve, and delete. Selection mode batches open / favorite / group / move / copy / delete / remove-from-staging over any mix of rows. An internal clipboard brings copy/cut/paste to the tree, the folder picker offers pinned + LRU quick picks with copy-as-text/Markdown/JSON export, and send to staging entries appear across the bookmark, history, tab-groups and stats views plus the context menus. Every view gets a shortcut bar of move-to-folder chips for one-click filing. A virtual scrolling lab switch joins the experiments (off by default).
  • Search history, on your terms: the record-search-history switch moves into the options Search group and now only toggles the area — recorded queries survive, re-enabling brings them back — the area gains an inline close × (the classic-experience preset closes it too), and a new recent searches to show count (3/5/10/20, default 5) crops the area to taste. The count is a synced preference like the recent-bookmarks count.

Performance

  • Chunked list rendering + content-visibility for heavy views: tab-groups first paint dropped from 1064 ms to 239 ms; the duplicates view regroups 57% faster; the dead-link scanner renders its results incrementally instead of in one blocking burst.
  • Surgical fold/collapse updates: folding or collapsing no longer rebuilds whole lists — only the affected rows change, so folding a big group stays instant.
  • One-time favicon placeholder parsing: the placeholder template is parsed once and reused, and i18n labels are hoisted per render instead of re-fetched for every row.
  • Leaner cold open: the popup no longer reads the entire local storage area (the multi-MB favicon cache included) into memory before first paint — only the keys the boot mirror serves are fetched, and the remembered popup size restores in a single read instead of two serialized ones.
  • Idle and typing costs removed: the sync engine skips its periodic full-tree scan when sync indicators are hidden, on Chrome builds without per-node sync state (< 138), and whenever the computed map is unchanged — bookmark event bursts now collapse into one batched recompute. Popup search typing is debounced like the omnibox always was, folder staging verdicts are computed once per render instead of re-walking every folder's subtree per row, and tree type-ahead no longer forces a layout read per keystroke.

Polish

  • One tree language across the tree, staging, recent and tab-groups views: every fold head sits on the tree's exact three-slot grid (chevron lead / icon well / title axis), and member rows indent so their icons line up under their head's title. The big section heads read at 14px with their titles parked on the icon-column edge below, every small group head now folds with the tree's thin chevron twisty (only the three biggest heads keep the solid triangle), and the staging workbench's dashed hierarchy lines were redrawn — they now descend from the parent icon's axis, start below the icon itself and use finer, quieter dashes. Tab-groups' selection mode drops its inert fold chevron, and the options page gets an alignment pass (dropdowns right-align to the card edge, standard form footers).

Fixed

  • Favicon dark-chroma guard: dark but vivid icons are no longer desaturated by the dark-mode contrast flip — only genuinely low-contrast icons get adjusted.
  • The hidden donation card no longer eats the first Esc on every popup open (it also silently extended the donation snooze) — the Esc layer cake follows the documented model again, and the tree's folder fold animation, accidentally frozen since a 2012 Chrome-dev-build workaround, plays once more.
  • Quick-add dedup everywhere: the Alt+Shift+S command and the page context menu no longer mint duplicate bookmarks when the URL is already saved, and the popup star no longer reports success when the add actually failed.
  • Palette and options hardening: F2/Delete on Chrome's root folders are refused the way the tree refuses them (previously an error dialog or a silent no-op after a confirmed delete), and the options page's Reset now asks before wiping every setting on every synced device.
  • Keyboard row-selector regression caused by chunked <ul> injection — keyboard navigation tracks the real row order again.
  • Recent-list scrollbar regression from content-visibility — the staging/recent view scrolls correctly to the end again.

v4.0.8

2026-08-21

New

  • Real site icons for bookmarks Chrome hasn't cached: this release fetches the actual site icon for bookmarks the browser has no cached image for — direct from the site first, then through a built-in third-party icon fallback list (favicon.run → icon.horse → DuckDuckGo) guarded by a per-provider circuit breaker and failover; while a dead-link proxy session is active, discovery re-runs through the proxy. The enriched cache is capped by a dynamic byte budget that halves-and-evicts under pressure. New Icons-group controls: "Fetch missing site icons" (on by default), "Third-party icon fallback" (also on by default), and a "Clear icon cache" action. Freshly fetched icons swap in with a subtle pop animation (silenced under reduced motion), and letter-avatar placeholders a fallback service returns for unknown domains are recognized by pixel fingerprint and skipped — the chain fails over instead of caching a gray letter tile for 30 days.
  • Favicon gallery: the options page's Clear icon cache row gains a View refreshed icons link opening a display-only gallery of every site whose icon vBookmarks refetched — each card shows the icon, where it came from (direct fetch, your proxy relay, or a named fallback provider), when it was fetched and its size, plus the bookmarks on that site with their folder path, dead marks, sync state and recorded opens. A compare toggle swaps every icon back to the placeholder it replaced — the difference the service makes is one click away.
  • Hide or disable views right from the tab strip: right-click a feature-view tab (or press ContextMenu/Shift+F10 on it) — Hide collapses the tab away like unchecking its Show … view option: the Alt+number jump goes, the palette command stays, and entering a hidden view from the palette shows a return-path toast; Disable greys out the show option and removes every entry point until re-enabled (each Show … view row in the options Views group carries the same Enabled/Disabled state). Hiding the Tree or Search tab hides the whole strip, and Alt+number numbering compacts over the visible set.
  • Options page regrouped per view: after General and Views, each view owns its options in tab order — Tree (bookmark-bar scope), Search (search-on-Enter), Recent (row count), Stats (visit statistics + search history) and Dead links (scan tuning + proxy) — with hidden placeholders for the upcoming tab-groups and duplicates settings (20 sections in all). Every per-view Show/Enable control stays together in the Views group.
  • Storage usage bar: the Icons group now shows how much of the ~10 MB chrome.storage.local quota the icon cache, everything else, and the free space each take (with a byte-formatted legend), updating live as data changes.
  • Header GitHub / Homepage links and version number in the top-right corner of the options page.
  • Backup includes the icon cache by default: a new "Include icon cache in backups" option packs the enriched per-site icon cache into settings export/import (on by default, so a full-fidelity backup travels with its icons); turning it off keeps backups small, and icons are re-fetched automatically.
  • Manual UI language switch: a language dropdown in the options page (General, under Theme) overrides the browser UI language — all 43 shipped locales with their native names, auto follows the browser; the palette's /lang <code> command does the same (/lang auto clears). Pages reload once to apply. (Layout direction still follows the browser UI language, so RTL locales render their strings left-to-right; manifest-level strings like the command names can't be switched.)
  • /version palette command: opens a metadata dialog — extension version, the matching announcement, browser name/version, OS, channel, UI language, user agent — with a one-click button copying the full JSON for bug reports.

Fixed

  • Favicon cache quota race: the byte budget is now enforced asynchronously, so a burst of concurrent fetches can no longer push the cache past the limit before the eviction runs.
  • Favicon discovery edge cases: site icons served as application/octet-stream (common on plain CDNs/static hosts) are now recognized by magic-byte sniffing instead of being rejected; page-HTML reads are capped at 200 KB; a host already cached no longer gets re-fetched when a placeholder renders before the storage hydrate lands; oversized session-only icons are capped in memory; and <link> extraction now honors <base href> and skips commented-out icon declarations.
  • Disabling icon fetch no longer punishes hosts: hosts whose fetch is aborted mid-flight by switching "Fetch missing site icons" off no longer get stamped with a 24 h failure marker — one toggle used to silence a batch of never-actually-failing hosts for a day.
  • Backup-import write safety: a failing settings write (quota or a transient error) now alerts clearly and skips the page reload instead of surfacing half-applied settings with no feedback; imported icon-cache entries are validated one by one (must be a data:image/ payload ≤ 96 KB), so a hand-edited backup can no longer bypass the cache budget.
  • No more zombie rows in the dead-link view: the marks-only "past marks" view (no scan run yet) now re-joins on bookmark events too — a deleted bookmark's row disappears at once and its ⚑ can no longer re-persist the deleted id; a bookmark deleted while a scan is running no longer resurfaces as a ghost row when the run finishes.
  • Cancelling a scan is final: a cancel landing while a cold-start resume / resume re-launch is still reading its state can no longer resurrect the cancelled run from a stale snapshot.
  • Announcement and "What's new" banners are keyboard-reachable: both new banners joined the managed Tab ring, the announcement's dismiss button is reachable, and Esc dismisses the announcement outright (same mark-as-seen semantics as its ×) — keyboard-only users could previously never get rid of it.
  • Icon-discovery data URLs and candidate cap: L2 tries at most the first 5 declared <link> candidates per page, a malformed data: candidate now skips just that item and the loop continues, and percent-encoded (non-base64) inline SVG data URLs decode correctly.
  • Options-page low-priority polish: the storage bar measures with getBytesInUse and debounces burst writes (300 ms); clearing the icon cache now confirms first and gives feedback when empty; backup filenames say -with-icons/-no-icons; backup export/import drops the runtime vbmDeadScan journal; the header GitHub link goes to the project repo, changelog links point at docs/README.md explicitly, and the header subtitle is valid markup.
  • Dead-link view low-priority polish: the second toolbar's "All" now reads "Any status" to disambiguate the two counters; rows without a per-row timestamp sort last under detection-time sorting; a hidden-by-filter marks-only view shows the "try another segment" hint instead of the first-scan call-to-action; the bare status pill "0" renders as "—"; and "Delete all" now covers the same visible set as "Select all" in the All view (result rows plus uncovered past marks).
  • Scan race hardening: a stale start chain tears down only its own generation's PAC, so it can no longer strip the proxy session from a newer scan; and an import rewriting deadMarks/deadMarkTimes while the side panel is open is reflected in the dead-link view immediately.
  • Dead-link view icon flicker on re-entry: re-entering an unchanged dead-link view rebuilt the whole list, blanking every favicon and replaying each swap — the view now keeps its DOM unless scans, marks or the bookmark tree changed while it was away, and enriched icons drop in instantly (the old fade-in made cached re-injections read as a vanish-and-reappear flicker).
  • IME composition Enter no longer opens the first search result: in the top search bar, the Enter that commits a Chinese/Japanese IME candidate now stays with the IME. Previously it was treated as a normal Enter and focused/clicked the first result; if the row was re-rendered before the click, the popup itself navigated to that bookmark's URL (an internal host could surface as a chrome-error://chromewebdata/ DNS error page).
  • Global palette wake-up no longer self-closes on cold opens: Ctrl/Cmd+Shift+K from another page could open the popup with the palette instantly closing again while the window's focus was still settling — the palette now waits for the popup to report focus before opening.
  • IME composition Enter ignored in the command palette too: the palette input gets the same composition guard as the search bar, so the Enter committing an IME candidate no longer executes the highlighted row.
  • Favicon probes can no longer raise browser auth prompts: direct icon-discovery fetches omit credentials and bypass the cache, so a 401 WWW-Authenticate site no longer pops a login dialog over the popup; a capture-phase guard also makes bookmark-row clicks always go through the actions layer — the popup itself never navigates to a bookmark URL.
  • Selection-mode keyboard focus: entering selection mode from the keyboard focuses the batch bar's first enabled control; exiting via the exit button or Esc on the toolbar returns focus to the restored Select button.
  • First-open banner clipping after a settings import: importing a saved popup width no longer makes the donation/announce banners lay out against the wrong containing block on the first open — controls that looked missing (Don't show again, the announce ×) are back.
  • Abandoned search queries stay abandoned: clicking the search bar's × (or deleting the query text) after switching away from the search view now actually persists the wipe — the stale query used to restore on the next popup open.
  • View-switch cleanup: switching views dismisses the hidden-view hint toast instead of letting it linger into the new view.
  • Tighter fuzzy ranking: fuzzy scoring gained a span penalty, so compact subsequence matches outrank scattered ones — query 123 now prefers 10.200.31.0 over vs1-…2/version3.html.
  • Search-history row menus anchor correctly: right-clicking a history row's clock/meta/time children opened the folder menu instead of the history menu — the nearest anchor is now preferred during target normalization; editBookmarkFolder is also guarded against a failed chrome.bookmarks.get callback.

Changed

  • Donation card redesigned: a redrawn gradient-heart illustration replaces the flat glyph, the card header is now the consolidated identity line "vBookmarks 4.0 — a complete remake with a modern design, packed with new features" linking the v4 guide (the card no longer carries per-version text), the ask is tighter, and a new Rate it button opens the Chrome Web Store listing — every review keeps the updates coming, and rating earns the same long quiet period as donating. The card's warm rose identity now reads clearly apart from the cool accent-bar announcement strips.
  • Settings storage audit landed: about forty small device-independent preferences (theme, UI language, behavior toggles, view and sort/filter prefs) moved to chrome.storage.sync and now follow the signed-in browser; a one-time migration on upgrade treats the sync area as authoritative and clears the old local copy only after a successful write, with a local fallback on the first post-upgrade service-worker start. Bookmark-id-keyed data (quick-add target, separators, dead marks, visit stats) deliberately stays local — Chrome does not keep bookmark ids stable across devices — and the oversized custom icon stays local too (the 8 KB per-item sync limit).
  • Icon fetch retries converge: cached icons never expire — they are served until the byte budget evicts them or you clear the cache, so no periodic mass re-fetch ever happens. A host whose icon can't be fetched is retried after 24 h, 3 days and 7 days, then given up until the cache is cleared; retries only fire when a placeholder row actually renders, never in the background. The favicon gallery's failed strip shows which state each host is in.
  • Announcement feed targeting: messages in docs/announce.json can now declare their audience with a version condition — an exact release ("4.0.8") or comparator conjunctions (">=4.0.0 <4.1.0") — or carry no version field at all for a general push to every version, so the feed works as a real push channel instead of only per-release notes. Pulls stay bounded (6 h cache, etag-conditional, only on popup/panel opens, every failure silent), a malformed condition drops the message instead of widening its audience, and dismissed messages never resurface. Minor-version release notes now carry just the summary + changelog link — the v4 guide link lives on the donation card's permanent header.
  • In-product announcement banner: the version-gate banner became a local "What's new" banner fired exactly once by the version gate on the 4.x → 4.0.8 crossing — no network, no dismiss; the remote announce feed (toggled by "Show in-product announcements") still runs as its network-dependent complement. The v4.0.8 banner announces this favicon-enhanced release and links the v4 guide and the changelog.
  • Options-page polish: trailing hints no longer sit far below their controls; list buttons share one visual weight with the page's other buttons; the storage bar gained a used/total overview row, distinct segment colors, per-segment share legend and keyboard-reachable tooltips; destructive actions (clear cache / clear stats / import / reset) render in the warning danger style; the header wraps gracefully at narrow widths.
  • Third-party icon fallback and icon-cache backups are on by default, so new users get working icons for sites Chrome can't reach directly and full-fidelity backups out of the box.
  • Announcement feed fallback chain: the in-product feed fetches through direct GitHub → your configured dead-scan proxy (marker-PAC) → a speed-tested top-5 of github.akams.cn mirrors — the mirror node list is refreshed and measured only when the mirror layer is actually reached (TTL/cooldown/circuit-breaker bounded) — so release notes also reach users who can't hit GitHub directly.
  • Dead-link view toolbar rebuilt: the idle controls are now three stacked icon rows — detection (last-scan time, rescan, clear, verdict filter), marks (mark/unmark-all, delete-all, select) and status (mark filter + sort) — every icon button titled, right edges column-aligned, and the clear-marks glyph is now a solid flag.
  • Duplicates view toolbar rebuilt: two aligned rows (strategy/scope/scheme; summary + apply-all + select) with an icon-only select-mode entry, plus a per-member delete on member rows (revealed on hover/focus) that regroups live — deleting one copy of a 2-member group dissolves the group, and deleting the pinned keeper falls back to the strategy pick.

Engineering

  • Favicon enrichment module: discovery chain, built-in provider list with per-provider circuit breaker + failover, cache + queue, capped HTML reads and session-only eviction.
  • Store publishing supports a trusted-tester grey release (TRUSTED_TESTERS); DEFAULT_PUBLISH restores a full rollout.
  • loadDotenv accepts inline comments (KEY=value # note).
  • Bookmarklet behavior verified in a real-browser harness (verify-bmlet.js).
  • Console probe added for the search-bar IME-Enter regression (scripts/console/probe-ime-enter.js).
  • Test suite at 77 files / 2510 cases, all green; the headless smoke gate gained a favicon-gallery section.

v4.0.7

2026-08-15

Fixed

  • Zoomed context menus always show at full height: the 4.0.5-era fix compressed the menu down to the space below the trigger row, so at zoom > 100 a below-midline right-click showed a shrunken menu. The menu now keeps its full height — clamped to a viewport-level scrollable box only when even the whole popup cannot fit it — and a right-click on an open menu's background re-opens it at the pointer instead of dismissing it, so consecutive right-clicks on a row always show the menu again (no more show/hide alternation).
  • Dead-marked list stays in sync with the toolbar filter: switching the toolbar between 全部 / 受限 / 死链 (All / Blocked / Dead) filters the result rows correctly again (仅受限 / 仅死链 used to be swapped), the marked list no longer echoes rows the active filter hides, and it renders after the result rows.
  • Imported backups no longer double-count "Previously marked": an import restores deadMarks and the last-scan cache as-is and the residue count derives from the §2.4 natural-residue rule — marks the restored scan verdicts dead/blocked ride their result rows and are never counted again as marks, so the All counter no longer doubles.
  • ↑/↓ keyboard navigation crosses the result list and the residue marked list: the "已标注" heading between the two sibling lists no longer blocks arrow-key travel, and the crossing works from a result row's inline buttons too; single-list views (tree/search/recent/stats/dupes) are unaffected.
  • Selection-toolbar counts, wide-row timestamps, consistent verdict colours: the batch-selection toolbar shows live category counts (All = dead + blocked + previously-marked), wide rows show each bookmark's detection time on the right, and dead (red) vs blocked (amber) now colour the pills, the mark buttons and the tree overlay alike.
  • Mark-status filter and status pills actually apply: the 已标注/未标注 filter had an inert dataset key that left it filtering nothing — it now really filters the result rows and the marked list; marked flags render filled, and status pills share a uniform width aligned with the timestamps.
  • Scan cancel/restart races fixed: cancelling or re-starting a scan while it is still in its asynchronous setup window (a cold-start resume, a second start, or a cancel landing before launch) no longer double-launches the scan, leaks the marker-PAC proxy session, or lets a cancelled run write a result cache — "cancel never happened" now holds regardless of timing.

Added

  • Dead-check view redesigned around a "Previously marked" category: the toolbar filter gains a Previously marked segment that switches to a marks-only view; in the default All view the marked list is appended below the scan results (italic heading to tell it apart), and items this scan still flags as broken move out of the marked list into the results keeping their ⚑ mark. The All counter now sums all three categories (dead + blocked + previously marked), not just this scan's result rows. Cancelling an in-flight scan switches to the Previously-marked view and raises a session-scoped, dismissible tooltip banner — prompting you to click Rescan to start a new check and to handle the bookmarks in the list below; the same banner appears after a finished scan when past marks this scan no longer flags as broken (reachable again, but marked before) remain below the results. The selection toolbar now applies to the marked list too, so marks can be batch-deselected.
  • Mark-status filter on a second toolbar: alongside the category filter, a second toolbar filters the view by mark status (已标注 / 未标注), applied to the result rows and the residue marked list together — 未标注 hides the residue list, since every residue row is marked. The batch/selection scopes (mark-all, delete-all, select-all/invert) all follow the visible set, and the two filters stay orthogonal.
  • Result sorting: a sort dropdown (Detection time — the per-row scan timestamp, default; Folder path; Mark time — a per-mark timestamp persisted alongside the marks) reorders the result rows and the residue list together. Sorting applies once a scan has finished only — a live scan keeps progressive tree order so rows never jump mid-scan — and old backups without timestamps fall back to the stable stored key order.
  • Amber reading for marked / blocked rows (4.1.0 task-1 A6/D1): a row's mark toggle that is already marked — or whose verdict is blocked (受限) — renders in the warning amber (same as the blocked pill) instead of the neutral accent, and the tree-view dead × overlay on marked bookmarks is amber too, so "restricted" reads apart from a dead-only row.
  • Clear scan results: a button resets the last dead-link scan (the ⚑ marks are kept as "previously marked") and re-raises the rescan banner, so you can start over without losing your marks.

v4.0.6

2026-08-15

Rollback to 4.0.4 — 4.0.5 shipped with two regressions (zoomed context menus, and the dead-marked list out of sync with the toolbar filter); its changes were recalled and reworked into 4.0.7.

v4.0.5

2026-08-15

⚠ Recalled / deprecated — superseded by 4.0.6 (rollback to 4.0.4); properly re-fixed in 4.0.7.

New

  • Low-contrast favicon inversion: a new faviconContrast option (Views group, on by default) detects favicons that sink into the active theme — dark glyphs on dark surfaces, light glyphs on light ones — and flips their brightness while preserving hue (invert(1) hue-rotate(180deg)), so brand colors survive. The detector keys off extreme-tone pixel shares rather than average brightness (which misread small dark marks on transparent backgrounds, e.g. thepaper.cn and GitHub) and deliberately spares self-inverting icons like x.com's white-on-black disc. It re-judges on OS light/dark flips under the auto theme, applies live in the always-on side panel, and was tuned against a 14-real-favicon × 4-theme render matrix.
  • Dead-link view empty state gains a "start scan" pill CTA — the first scan is one click from the empty view instead of a toolbar hunt.
  • Command palette result highlighting: matched characters are wrapped in <mark> again, so a row shows why it ranked.

Fixed

  • Palette keyboard traps: result rows no longer sit in the Tab order (they degraded ↑↓/Space into native scrolling and froze the highlight), and Tab now cycles a two-stop loop between the input and the Esc close button instead of escaping the palette and auto-closing it.
  • Stale .active marker swallowed keys: a leftover active class on a closed menu made the palette keep treating it as open.
  • Batch deletions report the real count: duplicate-cleaner removals that fail mid-batch are skipped and the summary toast counts only actual deletions (the dead-link side already did); applying a selection now exits selection mode.
  • Dead-link view robustness: bookmarks created mid-session (or restored by Undo) re-join the rows; the tab badge derives from the tree-joined result rows and can no longer resurrect a stale count on popup reopen; mark-all / selection entries key off the filtered row set like delete-all does.
  • Escaping gaps closed: an untitled bookmark's protocol-stripped URL fallback rendered unescaped (a pre-v4.0 leftover), and the shared escaper now covers & after a full caller audit proved no double-escaping path remains.
  • Folder menus in search results / the palette grey out content-dependent entries (open-all, tab group, sort) for empty folders, matching the tree — and a greyed state never leaks into the next menu.
  • Visual consistency: two-line row icons align with the tree's slot rhythm (search view included); delete affordances are uniformly red (search-history inline/menu deletes, remove-separator, the palette delete command, dupes clean) with a shared danger hover; sync dots and dead-link × badges use logical inset-inline-end, so RTL mirrors correctly.
  • Second-pass audit merge: the palette's custom-command rows now escape and validate slash/aliases (closing a sync-storage injection path); tree rebuilds park/restore the focused row and the focusID restore is guarded, so a rebuild no longer drops keyboard focus to the page body; popup focus memory covers the search-history area; the omnibox highlighter self-escapes regex metacharacters (a query like c++ no longer breaks highlighting) and highlights subsequence matches, so highlight and ranking agree; toolbar focus restore survives button-set changes (class+index instead of a bare position); the dead-link start row shows its focus ring only for keyboard focus (:focus-visible); dead-link selection rows drop the leftover ghost icon slot.
  • Colorful logos are no longer mis-inverted on light themes: a chroma guard keeps brand-color icons (e.g. the Chrome Web Store devconsole mark) from being flipped into a black-card wreck — the tuning matrix grew to 14 real favicons × 4 themes.
  • Dead-link marks survive a cancelled scan: with no result list on screen, marked bookmarks render as their own list (each individually unmarkable, plus a batch clear-all), and a cached result list surfaces the marks it doesn't cover — a cancelled run never strands a mark with no way to clear it.
  • Zoomed context menus never cover the right-clicked row: above 100% zoom an over-tall menu used to flip up over the trigger row, so the next right-click hit the menu itself and dismissed it (show/hide alternation) — the menu now clamps to the space below the row, including the viewport-scrollable case.

Changed

  • Three shared-module consolidations: nine verbatim htmlspecialchars copies → src/escape.js; the four list views' park/restore focus copies → src/list-focus.js (plus a unified row focus-target contract and the toolbar focus trio); omnibox and popup fuzzy ranking → src/fuzzy-core.js (one fzf-style implementation, with a fix for highlight offsets on characters like İ whose lowercase form changes length).
  • Focus memory obeys "Remember previous state": the remembered row is gated by the option and cleared at startup when off; the visible undo toast joined the Tab ring; dropdown-list rows are skipped by the row stops.
  • Batch confirmations name the stakes: duplicate-cleanup confirms name the keeper ("Keep ‹title› and remove the other N copies?"), and every batch delete/clean confirm across the dead-link and duplicates views appends the one-step-undo note (new key undoSingleStepNote, translated across all 43 locales).
  • Version bumped to 4.0.5 in manifest.json / package.json.

Engineering

  • Two independent audits of the full v4.0 → 4.0.5 delta plus their merge (the second pass caught what the first missed: a favicon theme observer that never installed, the palette injection path, tree focus park/restore) — both reports, the comparison/merge plan and the fused 4.1.0 design are archived in docs/review-4.0.5/; documentation (AGENTS.md, keyboard-model, the v4 guide) re-synced to the implementation.
  • The popup resize/zoom layer was extracted from neat.js into src/resize.js (pure decision kernels stay in src/resize-core.js), closing a stale drag-ceiling leak between drags.
  • Test suite at 67 files / 2078 cases, all green — new list-focus suite, rewritten favicon-contrast strategy tests, and new contracts for the deletion chains, menu greying, escaping and the i18n copy changes.

v4.0.4

2026-08-13

New

  • Empty-folder menu greying: folder context-menu items that depend on folder content (open-all, open-as-tab-group, new window / incognito, sort) grey out on empty or URL-less folders — only the add-type entries (bookmark / folder / separator inserts) stay enabled. Disabled entries are dimmed at half opacity so they read clearly as unavailable across every theme.
  • Unified focus restoration on popup reopen (focusSpot): the search bar, header buttons, in-view toolbar controls, view tabs and list rows restore their focus across a popup reopen through one model, gated by the "remember previous state" option.
  • Command palette returns focus on keyboard dismiss: closing the palette with Esc / Ctrl+K / the close button returns focus to the element that opened it (the search bar / tool button).

Fixed

  • Batch deletions (duplicates / dead-links) refresh the tree immediately — deleted rows no longer linger in the tree view until the popup is reopened.
  • Command-palette close focus misplacement (folded into the New item above).

Changed

  • Unified "what counts as a completed search": a query enters the recent-search history when it is consumed — opening a result, pressing Enter, the two-level Esc clear, leaving the search view, closing the popup, or revealing the row in the tree (R). The × clear button is the abandon path: it is not recorded, and it now clears the results pane too, so an unconsumed query leaves no trace.

Engineering

  • Test suite strengthened substantially: rewrote 11 pseudo-tests (copied-kernel / tautological / zero-assertion), filled 3 file-level coverage gaps, and extracted the resize / folder-sort / quick-add / donation / tool-button / wake-up / startup-flags / settings logic into testable modules.
  • New option-switch → behavior differential contract, parameterized-copy contract (tests/i18n-copy) and shared test-infrastructure helpers.
  • Introduced an ESLint progressive gate (error-level, in CI).
  • Added a real-browser smoke gate (zero console errors) to CI and the release pre-check, so an extension that crashes on load is caught before tagging.
  • Dead-code cleanup (the unused copy-all-titles-and-urls handler, the ineffective hide-editables toggle).

v4.0.3

2026-08-12

Fixed

  • Drag-and-drop sorting misaligned in zoomed mode (#59, #60): with the popup zoomed, a dropped row landed in the wrong place — the drag overlay's coordinates and the mouse-release drop-target hit-test did not account for the zoom level. Both are zoom-corrected now, with regression tests covering overlay layout, drop-into-folder sizing and the zoom-level reset.
  • The focus-restore highlight can be turned off (#58): reopening the popup used to refocus and flash the last-focused row on every open. The focus restore now follows the existing "Remember previous state" option — unchecked, the popup starts fully fresh, with no remembered highlight (and no remembered scroll or opened folders either).

Changed

  • "Remember previous state" now covers the last-focused row (#58): previously it only restored scroll position and opened folders; the focus restore is folded in, so a single switch controls the whole "where I was" restore. The option's description is updated in all 42 locales.

Polish

  • iGuge conflict documented (#53, #57): the iGuge proxy/acceleration extension actively disables any installed extension that declares the proxy permission and is not on its whitelist — so vBookmarks could be disabled on every Chrome restart when both are installed. Verified against the store-shipped CRX; the Dead-link section now explains the cause and what affected users can do, and we're coordinating whitelisting with iGuge. vBookmarks keeps its proxy permission (it is only ever used, temporarily and marker-only, during a dead-link scan).
  • Developer tooling: vitest bumped 1 → 3.2.7, clearing 6 Dependabot advisories (dev-only — no user impact); the modified-key i18n flow and the release process are documented in AGENTS.md.

v4.0.2

2026-08-11

New

  • Collapsible context-menu blocks (#48 follow-up): the crowded tab-group block (bookmark & folder menus) and the sort block (folder menu) collapse into a single submenu entry with a ▸ indicator. Two new Views toggles control them — collapseTabGroupMenu (tab groups, off by default) and collapseSortMenu (sorting, on by default). The flyouts reuse the whole context-menu keyboard protocol: →/Enter open, ←/Esc close one level at a time, ↑/↓ wrap inside the flyout on every platform.
  • Menus hardened at extreme zoom/DPI (#48): a right-click menu can no longer outgrow the popup — height clamps to the space below the search bar (overflow scrolls internally), width caps to the viewport, and a flyout that does not fit on one side flips or stacks below its entry. Menus stay usable at high browser zoom, high DPI and narrow popup widths.
  • Precision-first search ranking: results sort by match tier — exact > prefix > word-start > subsequence — with the added date demoted to a final tie-break, so an exact match can no longer be outranked by a newer, looser one. URL scoring strips the structural https:// and www. prefixes first, so https://github.com and https://www.github.com rank alike.

Fixed

  • #48: a right-click menu taller than the popup closed the instant it opened — focusing it scrolled the document and tripped the scroll-close listener. Menus now clamp to the viewport and scroll internally.
  • On macOS the slight finger movement of a right-click (two-finger tap / corner click) read as a scroll gesture and closed the freshly opened menu — a short window now tolerates small jitter scrolling right after open; a real scroll still closes it.
  • #56: the dark themes' blanket brightness(1.5) filter on every favicon is removed — the placeholder is already a fingerprint-replaced SVG, so the filter only overexposed real favicons.
  • Custom CSS no longer fails silently: when the CodeMirror editor does not load (CSP-blocked, etc.), a native textarea change event persists the edit.
  • Rapidly toggling the page right-click "Bookmark this page" switch no longer throws a "duplicate id" error — the remove→create cycle is serialized.
  • A collapsed flyout flipped to the wrong side when the popup body was narrower than the viewport — horizontal placement now keys off window.innerWidth.
  • The options-page tab-group collapse switch looked checked even though the feature is off by default (the stored '0' was read as truthy) — it now reflects the real state.

Polish

  • The 6 new collapsed-submenu keys are translated across all 42 locales; the collapsed entries keep just the ▸ indicator.
  • Developer tooling: scripts reorganized into harness / console / screenshots, the screenshot suite became a 4-theme matrix plus a 7-locale light i18n pass, console probes gained browser/platform identification, and the verify gate now also captures human-eye confirmation shots.

Changed

  • Version bumped to 4.0.2 in manifest.json / package.json; the design docs move on to the 4.1.0 slate.

v4.0.1

2026-08-08

New

  • Tab groups for folders & bookmarks: "open all as a tab group" now creates/joins the group in the service worker, so closing the popup mid-flight no longer aborts it. The folder/bookmark context menus add …and set name/color (a new-group dialog with a title and nine Chrome-style color swatches) and open into an existing group (a picker of your current tab groups); old Chrome or a vanished group falls back to a plain open.
  • Dead-link batch delete: the toolbar's red Delete all removes every row in the current filter (All / Dead / Blocked; the confirm shows the exact count); selection mode gains Delete selected — both run serially through the undo chain and end in one summary toast.
  • Folder sorting (#33): the folder menu offers Sort by name / Sort by date added as direct commands (recursive folders append "(recursive)") beside Sort options…; a new options-page Sorting group edits the same persisted sortOptions (by / folders-first / recursive). Sorting physically reorders the bookmarks (survives restarts), and every sort — recursive ones included — is undoable via toast (Undo replays the pre-sort snapshot level by level).
  • Palette theme shortcuts (#44): next to /theme <name>, the direct switches /dark /light /ink /paper apply a theme in one keystroke — the exact slash match now always wins the Enter row.
  • Page context-menu quick-add switch (#49): the "Bookmark this page" entry is now an independent Views toggle (quickAddContextMenu) — off removes it from every page on the fly, and the Restore the classic header preset covers it too.
  • Stats view merges recent visits into one list: bookmarked history rows merge into the main list wearing a solid ★ and their visit count in the pill; a toolbar Show unbookmarked checkbox (statsShowUnbookmarked) brings in the rest (one-click ☆ files them). The row end reads right-to-left: star → count pill → time.
  • Shared dropdown component: the Duplicates strategy/scope selects become a custom dropdown with one keyboard protocol (↓/Enter/Space open, Home/End jump to the first/last option, ←/Esc cancel, Tab applies) — the browser's native select no longer hijacks the arrows.

Fixed

  • #46: clicking a folder in search results opened the popup's own URL in a new tab — folder rows now jump in place (multi-class matching).
  • #47: the Stats "recent" time badge was crushed into a tiny pill — now plain muted text aligned with the path column.
  • "Open all as a tab group" could silently do nothing when the popup closed mid-creation (its tab callbacks were dropped).
  • Dead-link tab badge missing on cold start, and intermittently when opening the popup straight into the dead view — now pre-loaded and refreshed after the async read.
  • Clicking a ghosted/stale row (a folder deleted or synced away mid-redraw) no longer crashes.
  • A context-menu click on a re-rendered row (search-history remove / stats star) no longer throws.
  • Popup height could lock to 300 px while the tree was hidden — invisible trees are no longer measured.
  • Closing the side panel via the toolbar toggle could leave the popup unreachable — restored instantly (Chrome 142+) or via an alarm probe (114–141).
  • Ctrl/Alt+1-9 stopped working after Ctrl+2 parked focus in the search box — ordinary inputs no longer swallow view switches (only modal dialogs and the open palette do).
  • Ctrl+digit view switches could strand focus in the old view — focus now lands on the new view and the arrows work immediately.
  • In the palette, closing a context menu with ←/Esc stranded focus on a result row — it returns to the input.
  • "Reveal in tree" (R) left no keyboard focus — the revealed row now receives it.
  • New-tab-group dialog input misaligned with the color row — now the same width.
  • The packaged zip could silently miss new modules — the packaging script now resolves imports recursively.
  • Stats view overflowed horizontally at narrow widths — the path column now wraps instead of stretching.
  • A clean dead-link scan (zero dead rows) left no way to scan again from the view — the Rescan button now stays on the toolbar.
  • #50: middle-clicking a bookmark closed the popup even with "stay open" off — background opens no longer force-close the popup (only foreground opens honor the setting), matching the folder open-all behavior.
  • #51: a manually shrunken popup height kept "resetting" (auto-height re-grew it on the next tree click, so the popup could only ever grow) — once you drag the popup edge, auto-height steps back for the rest of the session.
  • #52: the custom toolbar icon did not survive a browser restart (chrome.action.setIcon is session-scoped) — the service worker now restores it on every cold start.
  • Duplicates toolbar regression: after opening the strategy/scope dropdown, ↓ from the button area could no longer enter the rows — a listbox option no longer steals the remembered-row .focus marker, and the toolbar rung's ↓ skips a marker parked inside a hidden listbox.
  • Menu actions and re-renders no longer strand keyboard focus: every menu dispatch closes the menu and returns focus to the owning row first, and the dupes/stats/recent views plus the search-history area park/restore a focused row across their innerHTML swaps — "set as keeper", deleting a history row and similar actions leave the arrow keys live.
  • The separator menu's lone "remove separator" entry used to be keyboard-unreachable by design — it is now bound to the menu walker and joins the Tab ring.

Polish

  • One 16 px line-art SVG icon set: solid/hollow star (stats), flag + trash (dead marks/deletes), check (dupes apply); danger actions stay red.
  • Dialogs: content width-capped and centered with a soft slide-in; the new-tab-group dialog gained breathing room and Chrome-style selected-color states.
  • Dead-link toolbar reordered: scan time → filter segment with counts (All 2 · Dead 1 · Blocked 1) → rescan / mark-all / unmark-all / delete-all / select.
  • Row metadata aligned across all list views: time column left, path column right.
  • Root-folder menus disable the actions Chrome refuses (delete, rename, add before/after, separator) instead of erroring.
  • Keyboard interaction hardening (menus, toolbar rungs, Home/End):
    • Context menus (all seven) wrap around on every platform — the old macOS no-wrap exception is gone — with Home/End jumping to the first/last enabled item.
    • A shared confirm/cancel protocol for menus and dropdowns: →/Enter/Space confirm, ←/Esc cancel, mirrored under RTL; confirming on a bare menu container or a greyed item is a no-op.
    • Toolbar rungs cycle at their edges like the tab strip; the tab strip's Home/End are now view-scoped (the current view's first/last row — they no longer switch views), and a row-less view crosses out to its anchor instead of trapping focus.
  • Dead-link proxy hint banner: the × sits top-right (aligned with the risk banner's close button) and the hint drops the "N direct failures" prefix.

Changed

  • Version handling refactored into src/version.js (full major.minor.patch comparison, threshold "crossed into" checks); the release is 4.0.1 — a silent patch, so existing 4.0 users don't get a "new version" card.
  • Keyboard model hardening: menu walking skips disabled and CSS-hidden items; a new focus-regression suite gates cross-view focus hand-offs.
  • Risk-banner re-arm gate is now major.minor-grained (it used to be major-only): a patch bump (4.0.0 → 4.0.1) stays silent, while 4.0 → 4.1 or 4 → 5 re-arms the banner once.
  • Dead-link proxy consolidation: the legacy relay URL template (deadProxyTemplate) is retired — values stored by older versions are cleaned out of storage automatically on upgrade. The options page's Dead scan group now manages your proxy server in place (add / test / save / clear; saving runs parse → permission → control → reachability probe and rejects unreachable servers), and the view's add-a-proxy hint strip can be dismissed with × and brought back from the Dead scan group's checkbox (hideDeadProxyStrip).

v4.0

2026-08-02

New

  • Six-view manager — Tree / Search / Recent / Stats / Dead links / Duplicates behind an icon tab strip with live count badges, per-view visibility toggles and Alt+1…6 jumps.
  • Search view — dual-zone layout (history top / results below) with re-runnable search history.
  • Recent view — coarse time groups and reveal-in-tree.
  • Local visit statistics — background collector, recently-visited section, one-click starring.
  • Dead-link scanner — dual-channel checks, progressive rendering, pause/resume/cancel and cross-view dead marks, running in the service worker so closing the popup mid-scan loses nothing. Its second channel now also supports your own proxy server (http/https/socks5): a one-click add button in the view's proxy strip validates the address and probes reachability (unreachable servers are rejected) before saving, change/remove live on the same strip, and the options page shows/clears the saved server. Routing uses a marker-matched temporary PAC so only the scanner's own probe URLs go through the proxy (other tabs untouched, settings restored on settle/cancel/popup close, crash residue swept by the service worker). The toolbar adds a dead·blocked summary line and a configure-a-proxy nudge when direct-failing rows have no proxy.
  • Duplicate cleaner — URL normalization, six keeper strategies, will-delete preview, undoable batch deletion.
  • Risk banner — both bulk tools show a one-time backup-first banner before first cleanup.
  • Command palette upgrades — Go commands per view, /theme <name>, /session, /options, aliases, custom slash commands (URL/template/folder-group/view-preset, synced), search bridge row, auto-close on blur.
  • Options — Views group, custom-commands group, settings backup/restore, responsive card layout.
  • Ink & Paper "fable" themes; quick-add star button; sync status presentation rework (quiet dots, localized tooltips, (Local)/(Synced) root labels, blocked-drag toast, working "highlight unsynced" dimming).

Polish

  • Selection modes in Dead links (batch mark/unmark) and Duplicates (batch group cleaning) with Esc to exit.
  • Tab/Shift+Tab region cycling including in-list toolbars, with per-region focus memory.
  • Duplicates member-row keys (Enter opens a copy, ← returns to the group head) and group-head menus.
  • Remember-last-view restore (default on) and count-badge, palette, quick-add and tool-button switches, plus a one-click Restore the classic header button.
  • Options and advanced options merged into a single page (old URL redirects).
  • Global quick-add shortcut (Alt+Shift+S).
  • ARIA roles on all context menus, aria-modal dialogs with a focus trap.
  • Lazy favicons; GitHub Actions CI.

Fixed

  • Search field click-through and unreliable native clear button.
  • Adding into collapsed folders is immediately visible.
  • Copy title/URL via the async Clipboard API (clipboardWrite permission added).
  • Non-empty folder deletion is confirm-gated again (with undo).

Changed

  • Repository reorganized (src/, pages/, css/, assets/, scripts/); obsolete release/ and MV2 leftovers removed (kept in git history).
  • All icons are inline SVG now.
  • Locale baseline grew to 306 keys with all 43 locales re-aligned through the scripts/i18n.py LLM pipeline.
  • Test suite grew to 1303 cases across 40 suites; Docker harness extended with a keyboard/view verification suite and multi-theme, multi-language screenshot captures.
  • The proxy permission is declared at install time (Chrome refuses it as an optional permission) and is exercised only while a configured proxy serves a running scan or the add-flow reachability probe — never when no proxy server is set or dead-link scanning is unused.

Earlier versions (pre-4.0)

Each release note lives in its own file under docs/changelog/ (Chinese: the .zh.md twin). Newer entries may migrate into that table over time — the files are the permalink home for shipped release notes.

Version Date Changelog
v3.7 2026-05-10 Changelog
v3.6 2024-01-08 Changelog
v3.5 2023-09-04 Changelog
v3.4 2023-02-14 Changelog
v3.3 2023-02-02 Changelog
v3.2 2020-09-12 Changelog
v3.1 2020-07-03 Changelog
v3.0 2019-08-22 Changelog
v2.9 2019-08-22 Changelog
v2.8 2019-05-06 Changelog
v2.6 2013-10-21 Changelog
v2.5 2013-08-30 Changelog
v2.4 2013-08-29 Changelog
v2.3 2013-04-09 Changelog
v2.2 2013-04-02 Changelog
v2.1 2012-12-12 Changelog
v2.0 2012-11-01 Changelog
v1.9 2012-08-19 Changelog
v1.8 2012-08-01 Changelog
v1.7 2012-06-26 Changelog
v1.6 2012-06-24 Changelog
v1.5 2012-06-21 Changelog
v1.4 2012-06-20 Changelog
v1.3 2012-05-25 Changelog
v1.2 2011-11-30 Changelog
v1.1 2011-11-16 Changelog
v1.0 2011-11-15 Changelog

Releases

Packages

Used by

Contributors

Languages