# LexAI — Improvement Recommendations

Prioritized findings from a full read of the codebase (2026-07-13). Grouped by theme;
each item notes rough effort (S/M/L) and why it matters.

## 🔒 Security & privacy

1. **Narrow host permissions (M).** The manifest requests `<all_urls>` host permission and
   injects the content script into every frame of every site — including banking, email, and
   internal apps. Consider `activeTab` + on-demand injection, or a user-configurable
   allowlist. This is also the #1 thing that slows Chrome Web Store review.
2. **The encryption is obfuscation, not protection (M).** `encKey` sits in
   `chrome.storage.local` right next to `apiKeyEnc`; anyone who can read storage can decrypt.
   Be honest in the UI ("stored locally, obscured") or derive the key from something not
   co-located (e.g. `chrome.storage.session` for the key, WebCrypto, or a passphrase). At
   minimum, don't over-promise "encrypted" security to users.
3. **Strip debug logging from production (S).** `content.ts` logs selection text and element
   values (`[LexAI captureForButton]`, `[LexAI Replace]`, etc.) to the page console — visible
   to the host page. Gate behind a `__DEV__`/`import.meta.env.DEV` flag.

## 🧹 Code quality & maintainability

4. **Collapse the duplicated provider layer (M).** Each provider exists twice —
   `callOpenAI`/`callOpenAIWithPrompt`, etc. — 8 near-identical functions. Refactor to one
   `callProvider(config, messages | systemPrompt, text)` with a small per-provider adapter
   describing `{ url, headers(config), body(model, system, text), extract(data) }`. Cuts
   `background.ts` roughly in half and removes the "update both copies" trap noted in CLAUDE.md.
5. **Extract shared UI/styling (M).** The Catppuccin palette and button styles are re-declared
   inline across content.ts, Options.tsx, Popup.tsx. Move colors/spacing into a shared
   `src/ui/theme.ts` (and reusable style factories) so a palette change is one edit.
6. **Remove dead dependencies (S).** `zustand` is installed but no store exists; `tailwindcss`
   + `autoprefixer` are present but inactive. Either wire them up or drop them to shrink the
   install and remove confusion.
7. **Centralize provider/model config (S).** The provider list, default models, and endpoints
   live in both `Options.tsx` (UI) and `background.ts` (calls). Put them in one shared module
   so the picker and the caller can't drift.

## ✅ Testing (biggest gap)

8. **Unit tests don't test real code (M).** `tests/unit/background.test.ts` only exercises the
   `chrome.storage` mock — it never imports `getSystemPrompt`, `decryptApiKey`, or the provider
   router. Extract those pure functions and test them directly (prompt normalization,
   `fix`→`grammar`, encrypt→decrypt round-trip, provider routing, error extraction).
9. **Fix or quarantine the e2e tests (S).** `tests/e2e/extension.test.ts` hard-codes
   `chrome-extension://[EXTENSION_ID]/...` — it cannot pass. Resolve the extension ID at
   runtime (read it from the service-worker target) or mark the suite `.skip` until fixed so
   CI green means something.
10. **Add a content-script DOM test (L).** The selection→snapshot→replace logic is the app's
    riskiest code and has zero coverage. A jsdom or Playwright test over textarea and
    contenteditable replace paths would catch regressions the current tests can't.

## ✨ Product / UX

11. **Make `max_tokens` adaptive (S).** It's hard-coded to `1024` everywhere; "Expand" on a
    long paragraph will truncate mid-sentence. Scale with input length or expose it in settings.
12. **Add response streaming (L).** Non-streaming means the user stares at "thinking…" for the
    full latency. Streaming tokens into the modal is the single biggest perceived-speed win.
13. **Accessibility (M).** Toolbar/modal buttons lack `aria-label`s, focus management, and
    keyboard navigation; the modal doesn't trap focus. Add roles/labels and Esc/Tab handling
    (Esc is partially handled already).
14. **React error boundaries + graceful storage failures (S).** Options/Popup call
    `createRoot(...).render()` with no error boundary; a throw yields a blank page.

## 🚀 Build / release

15. **Pin the toolchain (S).** Add an `.nvmrc`/`engines` field for Node 22 to match CI, and a
    `package.json` `packageManager` field. Local `npm run *` currently fails with no
    `node_modules` and no version guard.
16. **Version bump is a two-file manual step (S).** `version` must be edited in both
    `package.json` and `wxt.config.ts`. Add a script (or read one from the other) so a release
    can't ship mismatched versions — this has already caused churn in the git history.
17. **CI clones instead of checking out (S).** Both Gitea workflows `git clone` the repo into
    `/tmp` rather than using the checked-out workspace, and disable TLS verification
    (`http.sslVerify false`). Worth revisiting for speed and security once the runner setup
    allows a normal checkout.

## Suggested order

Quick wins first: **3, 6, 9, 11, 15, 16** (all S, mostly independent). Then the structural
refactors **4, 5, 8**, which make everything after them easier. Tackle **1/2** (permissions +
key story) before any serious Chrome Web Store push. Save **10, 12, 13** for a focused Phase 2.
