Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
6b7e5ce
feat(core): add updateBookmarks (bulk) and restoreBookmark pure mutat…
paperhurts May 25, 2026
51decc3
feat(web): add writeBookmarks to useGitmarksData hook
paperhurts May 25, 2026
c093e94
feat(web): add useSelection hook for multi-select state
paperhurts May 25, 2026
68082d9
feat(web): pure bulk-mutation factories built on core's updateBookmarks
paperhurts May 25, 2026
992de4e
feat(web): optional selection props on BookmarkRow + select-all header
paperhurts May 25, 2026
faf2449
feat(web): bulk actions bar (add tag, remove tag, set folder, delete,…
paperhurts May 25, 2026
dddafff
feat(web): list page bulk select + bulk actions wired to client.update
paperhurts May 25, 2026
a1079d6
feat(web): trash route with restore via bulkRestore
paperhurts May 25, 2026
1e88226
feat(web): Netscape HTML export utility (folder-aware, XSS-safe)
paperhurts May 25, 2026
d619831
feat(web): Layout Export button + Blob-download helper
paperhurts May 25, 2026
ceb5b27
feat(web): Trash nav link and wire Netscape export from every page
paperhurts May 25, 2026
084d407
test(web): cover /trash route under RequireSettings
paperhurts May 25, 2026
a55afd3
docs(web): document bulk operations, trash, and export (v2)
paperhurts May 25, 2026
467f59b
docs: add web UI v2 implementation plan
paperhurts May 25, 2026
8e3f875
fix(security): allowlist URL schemes; reject javascript:/data: at sav…
paperhurts May 26, 2026
dfd1ba9
fix(security): validate tag color regex before CSS interpolation
paperhurts May 26, 2026
c30ac97
fix(security): re-validate remote bookmarks.json and tags.json via Zod
paperhurts May 26, 2026
d1e9dd7
fix(security): URL-encode owner and repo in GitHub Contents API path
paperhurts May 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ Five packages are merged to main and working:
- `@gitmarks/extension-shared` (`packages/extension-shared/`) β€” canonical owner of the cross-browser extension code: popup, options, background, all of `src/lib/`, and the chrome/browser stub. 96 unit tests live here. Consumed by both browser shells via `workspace:*`. Uses `browser.*` via `webextension-polyfill`.
- `@gitmarks/extension-chrome` (`packages/extension-chrome/`) β€” Chrome MV3 shell. Manifest + Vite/crxjs build + Playwright e2e (4 passing, 2 skipped β€” see issue history for the activeTab/Playwright limitation). Source files are thin entries that re-export from `extension-shared` via its `exports` map.
- `@gitmarks/extension-firefox` (`packages/extension-firefox/`) β€” Firefox MV3 shell. Manifest + plain Vite build + manual smoke test (Playwright Firefox doesn't reliably drive WebExtensions). Targets Firefox 121+ for MV3 SW parity. Load via `about:debugging` β†’ "Load Temporary Add-on".
- `@gitmarks/web` (`packages/web/`) β€” Vite + React + Tailwind SPA. Read-side web UI: list, search, tag management. Talks directly to GitHub via `@gitmarks/core`. Hash routing (`#/setup`, `#/`, `#/tags`). 67 unit + component tests.
- `@gitmarks/web` (`packages/web/`) β€” Vite + React + Tailwind SPA. List, search, tag management, bulk operations, trash, Netscape HTML export. Talks directly to GitHub via `@gitmarks/core`. Hash routing (`#/setup`, `#/`, `#/tags`, `#/trash`). 105 unit + component tests.

Total: 228 unit + component tests across the monorepo, plus 6 Playwright e2e (4 passing, 2 skipped) in the Chrome shell.
Total: 272 unit + component tests across the monorepo, plus 6 Playwright e2e (4 passing, 2 skipped) in the Chrome shell.

Pending packages (in dependency order): web UI v2 (write + bulk ops), Safari.
Pending packages (in dependency order): Safari.

`spec.md` remains the source of truth for design decisions that aren't visible in the code.

Expand Down Expand Up @@ -83,9 +83,12 @@ Vite + React 18 + Tailwind 3 SPA. Read-side: list, search, tag management. Hash
- **Settings** (`src/lib/settings.ts`): Zod-validated `localStorage` wrapper for `{token, owner, repo, branch}`. Same PAT model as the extensions.
- **Client wrapper** (`src/lib/client.ts`): `makeClient(settings, fetch?)` builds a `GitHubClient`; `validateConnection` returns a discriminated `ValidateResult` (`ok-with-files` | `ok-no-files` | `auth-failed` | `repo-not-found` | `network-error`).
- **Data hook** (`src/hooks/useGitmarksData.ts`): loads `bookmarks.json` + `tags.json` on mount; tracks ETags and uses `readIfChanged` on `refresh()`. Seeds empty files on 404 so freshly-set-up users see the empty state, not an error. `writeTags(mutator, message)` delegates to `client.update("tags.json", …)` for 409 retry-replay.
- **Pure helpers** (`src/lib/data.ts`, `src/lib/tag-mutations.ts`): `visibleBookmarks` (filters tombstones), `searchBookmarks` (case-insensitive substring across title/url/tags/notes), `allUsedTags`, plus `addTag`/`renameTag`/`setTagColor`/`deleteTag`. All pure so they can be replayed inside `client.update`.
- **Routes** (`src/routes/`): `SetupPage` (PAT entry + Validate + Save), `ListPage` (search + tag-filter sidebar + BookmarkList), `TagsPage` (TagManager wired to `writeTags`).
- **Layout** (`src/components/Layout.tsx`): header, nav, status pill (loading/ok/warn/err), Sync-from-GitHub button.
- **Pure helpers** (`src/lib/data.ts`, `src/lib/tag-mutations.ts`): `visibleBookmarks` (filters tombstones), `deletedBookmarks` (returns soft-deleted within GC window), `searchBookmarks` (case-insensitive substring across title/url/tags/notes), `allUsedTags`, plus `addTag`/`renameTag`/`setTagColor`/`deleteTag`. All pure so they can be replayed inside `client.update`.
- **Routes** (`src/routes/`): `SetupPage` (PAT entry + Validate + Save), `ListPage` (search + tag-filter sidebar + BookmarkList), `TagsPage` (TagManager wired to `writeTags`), `TrashPage` (deleted bookmarks with restore).
- **Layout** (`src/components/Layout.tsx`): header, nav, status pill (loading/ok/warn/err), Sync-from-GitHub button, Export button.
- **Bulk operations** (`src/lib/bulk-mutations.ts`, `src/components/BulkActionsBar.tsx`, `src/hooks/useSelection.ts`): multi-select state; bulk add tag / remove tag / set folder / soft-delete; each fires one `client.update` call per action.
- **Trash** (`src/routes/TrashPage.tsx`, `src/components/TrashList.tsx`): filters `deleted_at != null` within the 30-day GC window; restore clears `deleted_at` via `bulkRestore`.
- **Export** (`src/lib/netscape-export.ts`, `src/lib/download.ts`): generates Netscape Bookmark File Format and triggers a browser download via Blob.

**Tag rename is decoupled from bookmark refs by design** β€” `renameTag` only mutates `tags.json`. Bookmark `tags[]` entries still reference the old name until updated by the extension's save path. Per `spec.md` Β§"`tags.json`": "Separate file so renaming a tag doesn't churn every bookmark."

Expand Down Expand Up @@ -120,10 +123,10 @@ pnpm --filter @gitmarks/extension-chrome e2e
3. βœ… Chrome native tree integration
4. βœ… Firefox MV3 add-on (`webextension-polyfill` + extension-shared) β€” issue [#23](https://github.com/paperhurts/gitmarks/issues/23)
5. βœ… Web UI v1: list / search / tag management β€” issue [#24](https://github.com/paperhurts/gitmarks/issues/24)
6. ⬜ Web UI v2: bulk operations + trash + export β€” issue [#25](https://github.com/paperhurts/gitmarks/issues/25)
6. βœ… Web UI v2: bulk operations + trash + export β€” issue [#25](https://github.com/paperhurts/gitmarks/issues/25)
7. ⬜ Safari (`safari-web-extension-converter`) β€” issue [#26](https://github.com/paperhurts/gitmarks/issues/26)

For next-piece-of-work: pick one of #23–#26. Each has a scope block in its issue description. The plan-driven workflow (`docs/superpowers/plans/YYYY-MM-DD-<feature>.md`) is the expected approach for anything larger than ~3 commits.
For next-piece-of-work: pick from the remaining open issues (#26 Safari). Each has a scope block in its issue description. The plan-driven workflow (`docs/superpowers/plans/YYYY-MM-DD-<feature>.md`) is the expected approach for anything larger than ~3 commits.

## Non-goals (do not implement)

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export) and Safari are next in the roadmap. See `spec.md` for the full design.
on the next 5-minute poll
- Concurrent edits from multiple devices reconcile automatically via
GitHub's file SHA + optimistic retry-replay
- 228 automated unit + component tests + 6 Playwright e2e (against real Chromium)
- 272 automated unit + component tests + 6 Playwright e2e (against real Chromium)
- Optional **tracking-param stripping** (utm_*, fbclid, gclid, etc.) at save time β€” opt-in via settings

## Packages
Expand Down Expand Up @@ -129,7 +129,7 @@ The load-bearing invariants:
- βœ… Tracking-param stripping (opt-in)
- βœ… Firefox MV3 add-on ([#23](https://github.com/paperhurts/gitmarks/issues/23))
- βœ… Web UI v1: list + search + tag management ([#24](https://github.com/paperhurts/gitmarks/issues/24))
- ⬜ Web UI v2: bulk operations + trash + export ([#25](https://github.com/paperhurts/gitmarks/issues/25))
- βœ… Web UI v2: bulk operations + trash + export ([#25](https://github.com/paperhurts/gitmarks/issues/25))
- ⬜ Safari ([#26](https://github.com/paperhurts/gitmarks/issues/26))

## Files in this repo
Expand Down
Loading
Loading