Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
44e28a9
feat(web): scaffold Vite + React + Tailwind package shell
paperhurts May 24, 2026
93240fd
fix(web): set Tailwind darkMode to class to match index.html
paperhurts May 24, 2026
85ca579
feat(web): add localStorage Settings with Zod validation
paperhurts May 24, 2026
44cbc57
feat(web): add GitHubClient factory + validateConnection
paperhurts May 24, 2026
c080cdb
fix(web): only swallow GitHubNotFoundError when probing tags.json
paperhurts May 24, 2026
716c605
feat(web): wire hash router with setup gate
paperhurts May 25, 2026
931bd98
fix(web): use Navigate for setup redirect and make test async
paperhurts May 25, 2026
0236b4e
refactor(web): delete redundant smoke test, document RequireSettings …
paperhurts May 25, 2026
3e65494
feat(web): setup form with PAT entry, validate, and persist
paperhurts May 25, 2026
b73f176
feat(web): layout chrome with nav and status pill
paperhurts May 25, 2026
9bdb6b2
feat(web): useGitmarksData hook with ETag conditional refresh
paperhurts May 25, 2026
d4804e5
feat(web): pure search + visibility helpers
paperhurts May 25, 2026
7baf764
feat(web): bookmark list rendering with tag chips
paperhurts May 25, 2026
cf8fe24
feat(web): list page with live search and tag filter sidebar
paperhurts May 25, 2026
0a1a0f9
feat(web): pure tag mutation helpers
paperhurts May 25, 2026
89b0b99
feat(web): tag manager UI writing to tags.json
paperhurts May 25, 2026
fa6c0d7
refactor(web): use React onChange for color input, fireEvent in tests
paperhurts May 25, 2026
c4c4859
docs(web): add package README and update root docs
paperhurts May 25, 2026
10646fe
fix(web): seed empty BookmarksFile on 404 instead of surfacing as error
paperhurts May 25, 2026
5fe9e3b
docs: refresh project status, test counts, and add @gitmarks/web arch…
paperhurts May 25, 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
24 changes: 21 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project status

Two packages are merged to main and working:
Five packages are merged to main and working:
- `@gitmarks/core` (`packages/core/`) — schemas, GitHub Contents API client with optimistic concurrency, ULID/URL helpers (incl. opt-in tracking-param stripping), pure mutation helpers, example fixtures. 65 unit tests.
- `@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.

Pending packages (in dependency order): Firefox build, web UI (read + search + tags), web UI (write + bulk ops), Safari.
Total: 228 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.

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

Expand Down Expand Up @@ -73,6 +76,21 @@ Each is a thin browser-specific shell over `@gitmarks/extension-shared`:
- Own HTML files (duplicated across shells because Vite needs them as build inputs — known follow-up)
- Chrome owns the Playwright e2e suite; Firefox relies on the manual smoke test in its README

### `@gitmarks/web` (`packages/web/`)

Vite + React 18 + Tailwind 3 SPA. Read-side: list, search, tag management. Hash routing (`createHashRouter`) so it deploys at any path on GitHub Pages or Cloudflare Pages. Cyan/magenta on dark per `spec.md`.

- **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.

**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."

**Test compromise worth knowing:** routing tests use `MemoryRouter + Routes/Route` rather than the production `createHashRouter` to sidestep a Node 24 / undici / jsdom AbortSignal incompatibility (`createHashRouter` triggers `new Request()` whose AbortSignal jsdom doesn't recognize). `RequireSettings` is exported from `App.tsx` for this purpose; the test setup file has a narrow `unhandledRejection` filter for the same root cause. Production wiring uses the real hash router and is verified by the manual smoke test in `packages/web/README.md`.

## Testing

- **Unit tests** (`packages/*/test/*.test.ts`): Vitest. The extension package uses jsdom + a `chrome.*` stub at `test/setup.ts` for tests that touch the chrome API. Pure logic is unit-tested in isolation.
Expand Down Expand Up @@ -101,7 +119,7 @@ pnpm --filter @gitmarks/extension-chrome e2e
2. ✅ Chrome MVP (toolbar save)
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)
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)
7. ⬜ Safari (`safari-web-extension-converter`) — issue [#26](https://github.com/paperhurts/gitmarks/issues/26)

Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ you control.
**Status:** Chrome extension is functional end-to-end (save via toolbar
button, two-way sync with the native bookmark tree, 5-min poll for remote
changes, automatic conflict retry). Firefox MV3 add-on shipping the same
source as Chrome via a shared package. Safari / web UI are next in
the roadmap. See `spec.md` for the full design.
source as Chrome via a shared package. Web UI v1 (read-side: list, search,
tag management) deploys as a static SPA. Web UI v2 (bulk ops + trash +
export) and Safari are next in the roadmap. See `spec.md` for the full design.

## Features (Chrome, today)

Expand All @@ -25,7 +26,7 @@ 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
- 162 automated tests (unit + Playwright e2e against real Chromium)
- 228 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 All @@ -36,6 +37,7 @@ the roadmap. See `spec.md` for the full design.
| `@gitmarks/extension-shared` | Cross-browser extension source — popup, options, background, lib/ helpers. Consumed by both browser shells via `workspace:*`. 96 unit tests live here. |
| `@gitmarks/extension-chrome` | Chrome MV3 shell. Manifest + Vite/crxjs build + Playwright e2e. Thin entry files import from `extension-shared`. |
| `@gitmarks/extension-firefox` | Firefox MV3 shell. Manifest + plain Vite build. Same source as Chrome via `extension-shared`. Load via `about:debugging`. |
| `@gitmarks/web` | Static SPA — list, search, tag management. Vite + React + Tailwind. Talks directly to GitHub via `@gitmarks/core`. Deploys to GitHub Pages or Cloudflare Pages. |

## Quick start (Chrome extension)

Expand Down Expand Up @@ -91,7 +93,7 @@ The repo is a pnpm workspace monorepo. Each package has its own
## Architecture

```
[Chrome ext] [Firefox ext] [Safari ext (planned)] [Web UI (planned)]
[Chrome ext] [Firefox ext] [Safari ext (planned)] [Web UI]
\ | / /
\ | / /
v v v v
Expand Down Expand Up @@ -126,7 +128,7 @@ The load-bearing invariants:
- ✅ Chrome native tree integration — listeners, reconcile, poll loop
- ✅ 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 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))
- ⬜ Safari ([#26](https://github.com/paperhurts/gitmarks/issues/26))

Expand Down
Loading
Loading