lupx-bookmark

LUPX Bookmark

A visual bookmark Speed Dial that replaces the Chrome New Tab page.

Available in the Chrome Web Store

Instead of the default blank tab, LUPX Bookmark greets you with a clean grid of bookmark cards — each showing a favicon and a title — organized into collapsible, drag-and-drop groups. Everything is stored locally in your browser; no account, no server, no tracking.


Why

The built-in Chrome bookmark bar is linear and hard to scan at a glance. Third-party Speed Dial extensions are either bloated, require sign-in, or inject ads. LUPX Bookmark is intentionally minimal: a single-purpose extension that does exactly one thing and stays out of your way.


Features


Tech Stack

Layer Technology
Language TypeScript 5 (strict mode)
UI framework React 18
Bundler Vite 8 + vite-plugin-web-extension
Styling CSS Modules
Chrome APIs chrome.storage.local · chrome.storage.onChanged · chrome.history · chrome://favicon2/ (favicon permission)
Testing Vitest + React Testing Library + jsdom
Lint / format ESLint (typescript-eslint) + Prettier, enforced via Husky + lint-staged pre-commit hook
Manifest Version 3 (MV3)
Package manager npm

No runtime dependencies beyond React. No Redux, no router, no UI library. Permissions declared in manifest.json: history, storage, favicon — note there is no bookmarks permission; bookmark cards are custom “slots” persisted in chrome.storage.local, not real Chrome bookmarks.


Getting Started

Prerequisites

Install dependencies

npm install

Development build (watch mode)

npm run dev

Vite rebuilds into dist/ on every file save.

Production build

npm run build

Type check

npm run typecheck

Lint & format

npm run lint        # eslint src
npm run lint:fix     # eslint src --fix
npm run format:check # prettier --check .
npm run format       # prettier --write .

Tests

npm test        # vitest run
npm run test:watch

A Husky pre-commit hook runs lint-staged (Prettier + ESLint) on changed files. CI (.github/workflows/ci.yml) runs typecheck, lint, test, format check, and build on every push/PR.


Loading the Extension in Chrome

  1. Open chrome://extensions
  2. Enable Developer mode (toggle in the top-right corner)
  3. Click Load unpacked
  4. Select the dist/ folder generated by the build
  5. Open a new tab — LUPX Bookmark replaces the default page

To apply code changes during development: after each save the dist/ folder is updated automatically; click the ↺ refresh icon on the extension card in chrome://extensions to reload.


Project Structure

lupx-bookmark/
├── public/
│   └── icons/
│       ├── lupx_logo.png     # Extension icon (16 / 48 / 128 px)
│       └── pin.svg           # Fallback favicon placeholder
├── src/
│   ├── newtab/
│   │   ├── newtab.html            # New Tab page entry point
│   │   ├── newtab.tsx             # React root — layout, drag state, event wiring
│   │   ├── newtab.module.css
│   │   ├── useAccordions.ts       # Accordion groups CRUD + chrome.storage.local
│   │   ├── useSettings.ts         # User settings + chrome.storage.local
│   │   ├── useBackground.ts       # Background rendering + file upload to storage.local
│   │   ├── useWallpapers.ts       # Built-in wallpaper gallery loader
│   │   ├── useFaviconCache.ts     # Persistent favicon cache in chrome.storage.local
│   │   └── FaviconCacheContext.ts # React context exposing the resolved favicon cache
│   ├── components/
│   │   ├── AccordionGroup/   # Collapsible group with drag handles
│   │   ├── BookmarkCard/     # Individual bookmark card (all 9 style variants)
│   │   ├── AddSlotModal/     # Add-bookmark dialog with history suggestions
│   │   ├── EditItemModal/    # Edit an existing bookmark's title/URL
│   │   ├── SearchBar/        # Search bar with engine picker
│   │   ├── SettingsPanel/    # Slide-in settings drawer (Style / Items / Backup tabs, rollback)
│   │   └── ErrorBoundary/    # Top-level render-error recovery UI
│   ├── types/
│   │   └── index.ts          # Shared TypeScript types and constants
│   ├── utils/
│   │   ├── favicon.ts        # favicon resolution: chrome://favicon2/ → Google S2 → pin.svg
│   │   └── backup.ts         # Backup export/import: file I/O, validation, merge strategies
│   └── test/
│       └── setup.ts          # Vitest + jsdom test setup
├── manifest.json
├── vite.config.ts
├── vitest.config.ts
└── tsconfig.json

Each hook and component has a co-located *.test.ts(x) file (omitted above for brevity).


Architecture Notes

All logic runs inside the New Tab page. There is no background service worker.


License

MIT — see LICENSE.