Files
LexAI/RECOMMENDATIONS.md
john kevin asprec f7fcd41341
All checks were successful
CI — Test & Build / Test & Build (push) Successful in 59s
feat: implement API key resolution and live model listing for providers
2026-07-13 13:56:48 +08:00

5.2 KiB

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

  1. 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.
  2. 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.
  3. 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.
  4. 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)

  1. 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, fixgrammar, encrypt→decrypt round-trip, provider routing, error extraction).
  2. 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.
  3. 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

  1. 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.
  2. 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.
  3. Accessibility (M). Toolbar/modal buttons lack aria-labels, focus management, and keyboard navigation; the modal doesn't trap focus. Add roles/labels and Esc/Tab handling (Esc is partially handled already).
  4. React error boundaries + graceful storage failures (S). Options/Popup call createRoot(...).render() with no error boundary; a throw yields a blank page.

🚀 Build / release

  1. 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.
  2. 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.
  3. 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.