Skip to content

Repository files navigation

Purrtab

A new tab page built from your bookmarks bar. Every top-level folder becomes a full-width row, and inside it each bookmark is a mini card: site logo on the left, title and domain on two lines to its right. Links from nested subfolders are pulled into their folder's row.

A new tab showing the bookmarks bar as full-width folder rows, each holding mini cards

The Mono theme, with sample bookmarks. Bookmarks lying loose on the bar get a row of their own, the count sits at the right end of every header, and the dashed bar at the bottom creates a folder.

Titles are cleaned for display: 169 · Inbox — Yandex.Mail shows up as Inbox. The browser's own bookmarks are never rewritten.

Cards can be dragged with the mouse, and folders can be renamed, reordered, created and deleted right on the page — see below.

The extension makes no network requests at all: no analytics, no favicons fetched from third parties.

Chrome and Firefox, Manifest V3.

Themes

Five visual languages. The switch is an iOS-style segmented control pinned to the top right corner: five labelled segments Mono · One UI · Clay · Dark · Grid, the active one lit by a sliding pill, any theme one click away. Left and right arrows walk the row from the keyboard and stop at both ends.

The choice is kept in localStorage and applied before the first paint, so a new tab never flashes the wrong theme — and the extension needs no storage permission.

Theme Look
Monochrome (default) black canvas, white hairlines, the hovered row inverts
One UI light, white cards, generous radii, blue accent
Clay pale lilac, soft volumes with a double highlight
One UI Dark the same language at night, blue accent on near-black
Night Grid a measuring grid behind translucent rows, monospace

Dragging

A card can be dragged inside its row to reorder it, or into another row to move the bookmark to a different folder. A line marks the insertion point and the target folder is outlined. Dropping onto empty space in a row appends the bookmark to that folder.

This rewrites the browser's real bookmarks, not just the picture on screen: bookmarks.move does the work. Hence the undo — Cmd+Z (or Ctrl+Z) puts the bookmark back where it was. The undo stack lives until the tab is reloaded.

One rule: dropping onto a card means taking its place. The point inside the card does not matter, neither does the direction — the neighbour simply yields its position and shifts aside. So a move two slots forward lands exactly two slots forward, and a round trip returns the bookmark where it started.

If a bookmark lived in a nested subfolder, it lands wherever its new neighbour lives — the layout flattens subfolders, and this is the only rule that never yanks a bookmark out of its subfolder unexpectedly.

After a move the neighbours glide to their new places (FLIP: only transform and opacity are animated) and the arriving card flashes briefly, so it is obvious where it landed. With the system "reduce motion" setting on, the animation is skipped.

Deleting a bookmark

A cross appears at the right edge of the card under the pointer and deletes the bookmark on one click, with no question asked. It keeps its place whether it shows or not, so a title is cut at the same point either way and nothing shifts under the cursor.

Nothing is destroyed silently: the message at the bottom offers Undo, and Cmd+Z does the same. Undo cannot resurrect a deleted bookmark, so it builds an identical one at the position the old one held — same folder, same slot, same title and address, new internal id.

Editing folders

Four actions, all of them changing the real bookmarks and all of them undoable with Cmd+Z:

Action How
Rename click the folder name; Enter commits, Escape restores
Reorder drag the row itself — anywhere outside a card counts as the handle
Create the wide bar under the list; the new folder opens its name field at once
Delete the bin in the header, which appears when the pointer is over the row

Deleting never destroys links: they move into the last folder, and a message at the bottom says how many went where. Deleting the last folder hands them to the one above it, which then becomes last. A new folder appears at the end of the bar, so a folder created and then thought better of leaves its bookmarks next door rather than at the top of the screen.

A folder is only removed once it is empty, so if there is nowhere to hand the links over — a single folder on the whole bar — nothing is deleted and the message says why. The same goes for a folder holding a subfolder: rows show links only, so the message names what is in the way.

The message carries an Undo button for eight seconds, and Cmd+Z does the same afterwards. Undoing a deletion recreates the folder at its old position and moves the links back into it; the name and the contents match, but the folder's internal id is a new one.

The row that holds bookmarks lying loose on the bar is not a real folder: it cannot be renamed, dragged or deleted.

Site logos

In Chrome they come from the browser's own cache through the _favicon API — hence the favicon permission in manifest.chrome.json. Not a single network request: the icons already live in the profile.

Firefox has no such API, so it shows the first letter of the domain on a plate instead. The detection reads the manifest (src/favicon.ts) rather than sniffing the user agent. If Chrome fails to hand back an icon, onerror swaps in the same letter.

Permissions: bookmarks, plus favicon in the Chrome build.

Development

npm install
npm test              # pure logic: layout + avatar + title + theme + dnd + folders
npm run build         # -> dist/chrome
npm run build:firefox # -> dist/firefox
npm run build:all     # both
npm run dev           # rebuild dist/chrome on every save

Each browser gets its own folder, because each needs its own manifest: Firefox refuses the favicon permission outright, and Chrome has no use for gecko.id. One shared folder meant the last build silently won.

npm run dev only rebuilds dist/chrome. To see the changes, hit "Reload" on the extension card in chrome://extensions and open a new tab.

Install in Chrome (locally, free)

  1. npm run build
  2. chrome://extensions → enable Developer mode
  3. Load unpacked → pick the dist/chrome folder
  4. Open a new tab

Do not rename or move the project folder afterwards: the id of an unpacked extension is derived from the absolute path. A different path means a different id, and the browser treats it as another extension.

Install in Firefox

Temporarily, until the browser restarts:

  1. npm run build:firefox
  2. about:debugging#/runtime/this-firefoxLoad Temporary Add-on
  3. Pick dist/firefox/manifest.json

The add-on disappears when Firefox restarts: that is what a temporary add-on is.

Building archives

npm run build:all && npm run pack

Produces build/purrtab-chrome.zip and build/purrtab-firefox.zip by zipping each build folder as it stands. pack never touches a manifest, so neither archive can end up with the other browser's one.

Layout of the source

src/
  types.ts        bookmark tree, column and theme types
  browser.ts      the only place that knows chrome.* from browser.*
  bookmarks.ts    finds the bookmarks bar among the roots
  layout.ts       tree -> columns (pure function, covered by tests)
  title.ts        cleans a bookmark title (pure function, covered by tests)
  avatar.ts       domain -> letter and colour (pure function, covered by tests)
  favicon.ts      favicon address from the browser cache, or null
  theme.ts        five themes, their order and the remembered choice
  dnd.ts          drop target maths (pure functions) and drag handlers
  folders.ts      folder maths: delete plan, receiver, folder drop target (pure functions)
  flip.ts         cards gliding to their new places after a reorder
  render.ts       columns -> DOM
  toast.ts        the message at the bottom, with an undo button when there is something to undo
scripts/
  make-icons.mjs  dependency-free PNG icon generator
  manifest.mjs    picks the manifest per browser
  pack.mjs        builds the zip archives

Two manifests, not one: Chrome warns about the unknown browser_specific_settings key, and Firefox without gecko.id loses its stable identifier when signed.

Not there yet

Search, a settings screen, Top Sites, expanding nested folders as a tree, deleting a folder together with its links, deleting several bookmarks at once, reacting to bookmark changes made in another window.

About

A new tab page built from your bookmarks bar: one full-width row per folder, each bookmark a mini card with the real site logo. Drag bookmarks between folders, rename and reorder folders in place. Chrome and Firefox, MV3.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages