# 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 `` 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.