Files
LexAI/docs/ARCHITECTURE.md
john kevin asprec acea99d7ad
Some checks failed
CI — Test & Build / Test & Build (push) Failing after 39s
feat: Implement Prompt Builder functionality in Popup and Options
- Added a new "Prompt Builder" tab in the Popup for generating AI prompts with customizable parameters.
- Introduced new state variables for managing prompt styles, personas, formats, and models.
- Enhanced the Options page to fetch and display models based on the provided API key.
- Updated the actions and types to include the new 'prompt' action and its associated parameters.
- Implemented migration logic for legacy plaintext API keys to encrypted storage.
- Updated the getSystemPrompt function to incorporate prompt parameters for better instruction generation.
- Added tests for the new functionality, including context menu entries and prompt generation logic.
2026-07-15 15:27:41 +08:00

73 lines
5.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Architecture — LexAI
> The current system and its important boundaries. Update in the same change that alters behavior; log the reason in `DECISIONS.md`.
## System at a glance
- **Shape:** Browser extension (Chrome, Manifest V3) with three cooperating contexts message-passed over `chrome.runtime`. No backend.
- **Stack:** WXT `^0.20` (wraps Vite), React 18 + TypeScript (Options/Popup pages only), tweetnacl/tweetnacl-util for key encryption, Zustand installed but unused.
- **Data stores:** `chrome.storage.local` (provider, model, `apiKeyEnc`, `encKey`, legacy plaintext `apiKey`). No server, no DB.
- **Hosting / deploy:** Chrome Web Store. Build output `.output/chrome-mv3/`.
- **Build / release:** Gitea CI (`.gitea/workflows/`) → typecheck → test → build → zip to Gitea registry; `v*.*.*` tag → Chrome Web Store deploy.
## Component map
```text
entrypoints/content.ts (content script, injected into <all_urls>)
• Detects selection: textarea/input (selectionStart/End) vs contenteditable/DOM (Range API)
• Renders floating toolbar + result modal + toasts (inline-styled, appended to document.body)
• Snapshots selection state BEFORE any async call; Replace uses the snapshot
• Sends { type: 'ANALYZE_TEXT', payload: {text, action, style} } to background
entrypoints/background.ts (service worker — the LLM proxy)
• onMessage: ANALYZE_TEXT and COPY_AS (returns true to keep async channel open)
• Reads provider/apiKey/apiKeyEnc/encKey/model from chrome.storage.local
• Decrypts key (tweetnacl secretbox), routes to the correct provider fetch
• Registers right-click context menus (action × style) on install
entrypoints/options/Options.tsx (React settings page)
• Provider + model + API key form; encrypts key → apiKeyEnc/encKey in storage
entrypoints/popup/Popup.tsx (React toolbar popup)
• Standalone text box → same ANALYZE_TEXT flow; shows config status; links to Options
```
| Component | Responsibility | Owns (paths) | Talks to | Notes |
| --- | --- | --- | --- | --- |
| content script | selection, toolbar/modal UI, replace | `entrypoints/content.ts` | background via messages | DOM-timing-sensitive; snapshot before await |
| background SW | LLM proxy, key decrypt, routing, context menus | `entrypoints/background.ts` | provider APIs, storage | only context allowed to fetch providers |
| options page | provider/model/key config + encrypt | `entrypoints/options/Options.tsx` | storage | React |
| popup | standalone analyze + status | `entrypoints/popup/Popup.tsx` | background via messages | React |
| shared utils | reference notes, future shared code | `src/utils/**` | — | see REFERENCE_NOTES.md |
## Boundaries and contracts
- **CORS/key boundary:** Content script and popup **must not** call provider APIs. All provider `fetch` and key handling live in `background.ts`. Route through `ANALYZE_TEXT` / `COPY_AS`.
- **Message contract:**
- `ANALYZE_TEXT` accepts **both** `{ payload: {text, action, style} }` and flat `{ text, action, style }` — keep both if you touch the handler.
- `action``grammar | rephrase | shorten | expand | explain`. Context menu/popup emit `fix`, normalized to `grammar` by `getSystemPrompt`.
- The `onMessage` listener **must `return true`** to keep the async channel open; removing it silently breaks every response.
- **Provider layer:** Each provider is duplicated — `callX` (system prompt from action) and `callXWithPrompt` (arbitrary prompt, used by COPY_AS). Request-shape changes usually need both. (Known smell — see DECISIONS + TASKS.)
- **DOM guard:** Every injected node carries `data-lexai="true"`; handlers check `closest('[data-lexai="true"]')` to avoid self-triggering. `z-index: 2147483647` keeps UI above host pages.
## Data model (essentials)
- **Storage keys:** `provider`, `model`, `apiKeyEnc`, `encKey`, `apiKey` (legacy plaintext, back-compat only).
- **Sensitive data:** the LLM API key. Prefer the encrypted path; never log it; never transmit except to the user's selected provider. Don't drop the plaintext fallback without a migration.
## Cross-cutting concerns
- **Config/secrets:** provider list + default models + endpoints currently duplicated across `Options.tsx` and `background.ts` (drift risk — see TASKS).
- **Observability:** intentional `[LexAI …]` console logs in content.ts replace path (should be gated behind a DEV flag — see TASKS).
- **Testing:** Vitest (jsdom) unit + Playwright e2e. Unit tests currently exercise the `chrome.storage` mock rather than importing real handlers; e2e has `[EXTENSION_ID]` placeholders and won't pass as-is (see TASKS/EVALS).
## Known constraints and debt
- Tailwind inactive; all UI is inline style objects (WXT PostCSS never wired). Do not assume Tailwind classes work.
- No shadow DOM; UI injected directly into `document.body`, isolated only by `data-lexai` + max z-index.
- Version bump is a manual two-file edit (`package.json` + `wxt.config.ts`).
---
*Record non-obvious choices in `DECISIONS.md`; keep exposure current in `attacksurface.md`.*