diff --git a/.eslintrc.cjs b/.eslintrc.cjs index d4762b6..0102ecb 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -10,7 +10,7 @@ module.exports = { ], parser: '@typescript-eslint/parser', parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - plugins: ['react-refresh', 'jsx-a11y'], + plugins: ['jsx-a11y'], settings: { react: { version: '18.3' } }, rules: { 'react/react-in-jsx-scope': 'off', diff --git a/README.md b/README.md index 650af38..848e700 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,16 @@ A Chrome extension for inspecting, editing, saving, and replaying URL query para ## Features - **Live parameter editor** — parses the active tab's URL, shows each query param as an editable row, with one-click navigation to the edited URL. -- **Type-aware inputs** — `true` / `false` values render as a toggle switch with a `BOOL` badge; everything else is a monospace text field. -- **Highlighted URL preview** — full URL shown at the top with parameter keys in the accent color, base URL and separators muted. +- **Type-aware inputs** — `true` / `false` values render as a toggle switch next to the literal value (original casing preserved, so `TRUE` toggles to `FALSE`); JSON objects and arrays get a drill-down editor; everything else is a monospace text field. +- **Filter parameters** — live substring search over parameter keys, for URLs with dozens of params. +- **Highlighted URL preview** — full URL shown at the top with parameter keys emphasized and separators muted; editable directly. +- **Structured JSON editing** — drill into nested objects and arrays with a breadcrumb stack, or edit the raw JSON. - **Save & group** — persist URLs into named groups (stored locally via `chrome.storage.local`), reload them into the editor with one click. - **Copy / Apply / Reset** — copy the current URL, replace the active tab's URL, or snap back to the URL that was open when the popup first loaded. -- **Dark, light, and system themes** — all colors meet WCAG 2.2 AA contrast. +- **Accent color** — Settings offers ten accent swatches that tint the logo, parameter badge, URL keys, toggles and primary buttons; "Reset to monochrome" returns to the base look. Persisted locally. +- **Follows your system appearance** — light and dark switch with the OS, live. There is no theme setting to get out of sync. - **Full keyboard navigation** — every interactive element is reachable via Tab, with visible focus rings and sensible shortcuts. -- **Screen-reader friendly** — semantic HTML, ARIA live region for status announcements, role-correct toggle/switch/dialog semantics. +- **Screen-reader friendly** — semantic HTML, a single ARIA live region (the toast doubles as it, so nothing is announced twice), role-correct toggle/switch/radiogroup/dialog semantics. ## Planned features @@ -28,9 +31,8 @@ A Chrome extension for inspecting, editing, saving, and replaying URL query para | `Enter` in a row field | Commit / advance focus | | `Cmd`/`Ctrl` + `Enter` | Apply edited URL to the active tab | | `Cmd`/`Ctrl` + `S` | Open the saved URLs drawer | -| `Esc` | Close the drawer | +| `Esc` | Close the open panel / step one level out of an expanded JSON value | | `Space` on a `BOOL` toggle | Flip the value | -| `←` / `→` on the theme switch | Cycle light → system → dark | ## Tech stack @@ -38,8 +40,8 @@ A Chrome extension for inspecting, editing, saving, and replaying URL query para - **React 18** with TypeScript (strict mode) - **Vite** + `@crxjs/vite-plugin` for HMR during popup development - **Zustand** for state (small footprint, test-friendly) -- **CSS Modules** + CSS custom properties for theming -- **Vitest** + React Testing Library (infrastructure ready; tests not yet written) +- **CSS Modules** + CSS custom properties for theming, with an `oklch` monochrome token set in `src/styles/tokens.css` +- **Vitest** + React Testing Library (unit tests cover URL parsing, param typing, structured values, storage, and the store) ## Permissions @@ -47,9 +49,8 @@ The extension requests only what it needs: | Permission | Why | | --- | --- | -| `activeTab` | Read and update the URL of the currently focused tab | -| `tabs` | Query the active tab's URL when the popup opens | -| `storage` | Persist saved links, groups, and theme preference locally | +| `activeTab` | Read and update the URL of the currently focused tab (granted per user gesture — no standing access to browsing history) | +| `storage` | Persist saved links, groups, and the accent color locally | No ``, no `clipboardWrite` (popups are secure contexts and a user-gesture click is enough for `navigator.clipboard.writeText`). @@ -135,15 +136,36 @@ The following secrets must be set in the repository settings: 2. Open a PR from `main` → `release`. 3. Merge the PR — the workflow bumps the version, builds, zips, uploads to the store, and tags the release automatically. +## Design system + +The UI follows a monochrome, Linear-inspired design (see `src/styles/tokens.css`). + +| Aspect | Value | +| --- | --- | +| Popup size | 380 × 600 px | +| Palette | `oklch` with zero chroma — hierarchy from lightness, border alpha, and weight | +| Radii | 6–7px controls · 8px larger inputs · 10px cards · 20px pills | +| Type | Inter (UI) · JetBrains Mono (URLs and values) | +| Icons | Inline Feather/Lucide-style outlines: 24-unit viewBox, 2px stroke, round caps | + +Three deliberate deviations from the design handoff: + +- **Height is 600px, not 640px.** Chrome hard-caps extension popups at 800 × 600; a 640px panel is simply clipped. The layout is a flex column with a scrolling middle band, so it renders faithfully at 600. +- **Reset does not open a `window.confirm()` dialog.** Modal JS dialogs are suppressed inside MV3 extension popups, so the confirmation would never appear and Reset would silently do nothing. The handoff lists this as a tweakable behavior; it is off. +- **Accent labels are not fixed to near-white.** The handoff specifies `#FAFAFA` text on the accent-colored buttons, but every swatch in the palette fails WCAG AA that way — from 3.4:1 (Violet) down to 2.2:1 (Lime), against a 4.5:1 requirement for 13px text. The label color is therefore chosen per swatch by luminance, and accent-as-text (logo, badge, URL keys) is darkened or lightened just enough to clear 4.5:1 on the current panel. Solid fills keep the exact swatch the user picked, so a button always matches its swatch. See `src/lib/contrast.ts`. + +Fonts are referenced by family name and fall back to the system stack — the extension ships no webfonts, because the MV3 content security policy blocks remote font CDNs. To match the reference typography exactly, self-host the Inter and JetBrains Mono `woff2` files under `public/` and add an `@font-face` block. + ## Accessibility notes -- Every interactive element is ≥ 28×28 CSS px (WCAG 2.2 SC 2.5.8). -- Focus indicators are preserved via `:focus-visible` outlines; `outline: none` is never used without a replacement. +- Interactive controls are ≥ 24×24 CSS px (WCAG 2.2 SC 2.5.8 minimum), with header and footer actions at 28px and larger. +- The reference design signals focus only by lightening an input border. That is kept, and a `:focus-visible` outline is layered on top so keyboard users get a real indicator; `outline: none` is never used without a replacement. - Every input has a `