The tab strip used flex: 1, which stretched it to fill the popup width. scrollWidth then equaled clientWidth, so each ResizeObserver pass set the popup wider, which stretched the strip wider, and so on until the screen clamp. Switching to flex: 0 1 auto + min-width: 0 lets scrollWidth report intrinsic content width while still allowing the strip to shrink and scroll horizontally if the popup is forced narrower than its tabs.
Folder Bookmark Tray
A Windows system-tray app for bookmarking folder paths with quick-action buttons (Explorer, Terminal, Claude, Visual Studio).
Personal utility, Windows-only.
Screenshots
TBD — drop popup + context-menu screenshots in assets/ and link them here.
Install / dev / build
npm install # also runs `npm run icon` to generate the tray PNG
npm run dev # Vite (5173) + Electron, hot reload
npm run build # vite build → dist/
npm run start # run Electron against built dist/
npm run dist # build + electron-builder NSIS installer → build/
npm run icon # regenerate assets/tray-icon.png
Usage
- Left-click the tray icon → popup with the bookmark list. Each row shows alias + path + four action buttons and a
⋯overflow menu (Copy path / Edit / Remove). - Right-click the tray icon → native context menu. Each bookmark is a submenu with the same actions, plus Browse for folder… (quick-add via folder picker) and Manage… (opens the popup).
- Pin toggle in the popup header keeps it open after focus loss; pinned position is persisted across runs.
- Auto-fitting popup: width is fixed at 360px; height grows with content from 180px up to a 600px cap, then scrolls internally.
Action buttons
| Button | Behavior |
|---|---|
| 📁 Explorer | shell.openPath(path) |
>_ Terminal |
wt.exe -d <path> (Windows Terminal); falls back to cmd.exe on ENOENT |
C Claude |
cmd.exe /c start "" /D <path> cmd.exe /K claude (detached, ignored stdio, unref'd) |
VS Visual Studio |
Finds the first *.sln in the folder and opens it via shell association — uses whichever VS the user has registered for .sln. Disabled with a tooltip when no .sln is present. |
| Copy path | clipboard.writeText(path) |
Config file
Persisted to app.getPath('userData')/config.json. On Windows that's:
%APPDATA%\Folder Bookmark Tray\config.json
Schema:
{
bookmarks: [
{ id: "uuid", alias: "string", path: "string", createdAt: "ISO8601" }
],
pinned: false,
pinnedBounds?: { x, y }, // remembered drag position when pinned
popupHeight?: number // last auto-fit height, clamped 180..600
}
Layout
electron/
main.js tray + popup window + IPC handlers + context menu
preload.js contextBridge → window.bookmarks
launchers.js Explorer / Terminal / Claude / Visual Studio / clipboard / folder inspect
src/
main.jsx, App.jsx
components/
Popup.jsx ResizeObserver auto-fits Electron window height
BookmarkList.jsx
BookmarkRow.jsx view + inline edit, ⋯ menu, VS-disabled-when-no-sln
AddBookmarkForm.jsx
styles/index.css
scripts/generate-icon.js produces assets/tray-icon.png at install time
assets/tray-icon.png orange folder pictogram, 32×32
Stack
Electron 33, React 18, Vite 5. NSIS installer via electron-builder (appId: com.dbeuttel.folder-bookmark-tray). No native deps.
Non-goals
- macOS / Linux support
- Cloud sync of bookmarks
- Drag-to-reorder
- Tagging or folder grouping
Why this exists
Sibling utility to claude-usage-tray — same window chrome, same dark-theme tokens, same userData/config.json persistence pattern, same tray-anchored popup behavior. Where ClaudeTracker is a usage dashboard with a saved-paths side feature, this app makes folder bookmarks the whole point: a fast keyboard-free way to jump from the tray into Explorer, a terminal, Claude, or Visual Studio at a known directory.