feat: add LexAI status bar and suggestion panel
Some checks failed
CI — Test & Build / Test & Build (push) Has been cancelled
Some checks failed
CI — Test & Build / Test & Build (push) Has been cancelled
- Implemented a status bar item for LexAI with dynamic status updates (ready, processing, notReady). - Created a suggestion panel for displaying and interacting with AI-generated suggestions. - Added functionality for accepting, regenerating, and discarding suggestions within the suggestion zone. - Introduced configuration options for writing style, prompt patterns, personas, and formats. - Integrated progress indicators for long-running tasks and improved user feedback. - Established TypeScript configuration for the vscode package.
This commit is contained in:
@@ -1,72 +1,62 @@
|
||||
# Architecture — LexAI
|
||||
# Architecture
|
||||
|
||||
> The current system and its important boundaries. Update in the same change that alters behavior; log the reason in `DECISIONS.md`.
|
||||
> The current system and its important boundaries. Describe what *is*, not aspirations.
|
||||
|
||||
## 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.
|
||||
- **Shape:** dual client surfaces (Chrome MV3 extension + VS Code extension) sharing portable LLM core; no LexAI backend
|
||||
- **Stack:** TypeScript; Chrome via WXT ^0.20 + React 18; VS Code via `packages/vscode` (esbuild bundle); Node 22
|
||||
- **Data stores:** Chrome `chrome.storage.local` (encrypted key + prefs); VS Code Secret Storage (API key) + `lexai.*` settings
|
||||
- **Hosting / deploy target:** Chrome Web Store (Gitea CI); VS Code Marketplace not wired yet (local VSIX / extension host)
|
||||
- **Build / release:** Chrome `npm run build` → `.output/chrome-mv3/`; VS Code `npm run vscode:build` → `packages/vscode/out/extension.js`
|
||||
|
||||
## 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
|
||||
shared: src/lib/{providers,actions,types} (+ crypto/messaging Chrome-only)
|
||||
|
||||
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
|
||||
Chrome:
|
||||
content.ts ──ANALYZE_TEXT──> background.ts ──fetch──> provider API
|
||||
options/popup (React) ──storage──> chrome.storage.local
|
||||
|
||||
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
|
||||
VS Code:
|
||||
extension.ts (commands/menus) ──callProvider──> provider API
|
||||
│ ▲
|
||||
└── secrets / settings ────────┘
|
||||
```
|
||||
|
||||
| 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 |
|
||||
| Shared core | Actions, prompts, provider adapters | `src/lib/providers.ts`, `actions.ts`, `types.ts` | Provider HTTPS APIs | No Chrome/VS Code imports |
|
||||
| Chrome content | Selection, toolbar/modal, replace | `entrypoints/content.ts` | Background via messaging | DOM timing; `data-lexai` |
|
||||
| Chrome background | Decrypt key, call providers, menus | `entrypoints/background.ts` | `chrome.storage`, providers | CORS + key isolation |
|
||||
| Chrome Options/Popup | Settings + standalone analyze | `entrypoints/options`, `popup` | Storage + background | React; inline styles |
|
||||
| VS Code extension | Commands, context menu, replace | `packages/vscode/src/**` | SecretStorage, settings, providers | Bundles `@lib` via esbuild |
|
||||
| Chrome crypto/messaging | tweetnacl key path; safe chrome wrappers | `src/lib/crypto.ts`, `messaging.ts` | `chrome.*` | Not used by VS Code |
|
||||
|
||||
## 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.
|
||||
- **Trust boundaries:** page DOM (Chrome) and editor buffer (VS Code) are untrusted text; API key never logged; only user’s chosen provider receives text/key
|
||||
- **Message contract (Chrome):** `ANALYZE_TEXT` (payload + flat), `COPY_AS`, `LIST_MODELS` — see `src/lib/types.ts`
|
||||
- **VS Code commands:** `lexai.{fix,rephrase,shorten,expand,explain,prompt,setApiKey,clearApiKey,showStatus}`
|
||||
- **Internal imports:** VS Code may import `@lib/providers|actions|types` only — not `crypto` / `messaging`
|
||||
- **External dependencies:** OpenAI, Anthropic, Groq, OpenRouter chat + models endpoints
|
||||
|
||||
## 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.
|
||||
- **Config:** provider, model, writing style, keyProvider
|
||||
- **Sensitive:** API key — Chrome encrypted blob + encKey; VS Code Secret Storage
|
||||
- **Legacy:** Chrome plaintext `apiKey` fallback until migrated
|
||||
|
||||
## 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).
|
||||
- **Authn/z:** none (BYO key)
|
||||
- **Observability:** console debug in Chrome replace path (pre-release); VS Code notifications
|
||||
- **Feature flags:** none
|
||||
- **Config:** Chrome storage schema; VS Code `contributes.configuration` `lexai.*`
|
||||
|
||||
## Known constraints and debt
|
||||
## Open architectural risks
|
||||
|
||||
- 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`.*
|
||||
- Chrome and VS Code settings are not synced
|
||||
- VS Code v1 has no Prompt Builder / Copy As / floating toolbar
|
||||
- Relocating Chrome into `packages/chrome` deferred — root remains the WXT app
|
||||
|
||||
Reference in New Issue
Block a user