Some checks failed
CI — Test & Build / Test & Build (push) Failing after 39s
- 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.
5.6 KiB
5.6 KiB
Decisions — LexAI
Lightweight ADRs. One entry per material decision: what, why, what was rejected, when. Newest at top. Supersede rather than rewrite.
Log
D-2026-03-06-01 — BYO-LLM-key, no backend
- Status: accepted
- Context: Hosted writing assistants cost a subscription and route user text through a third party. Target users already hold LLM API keys.
- Decision: No LexAI server. The background service worker calls the user's chosen provider (OpenAI/Anthropic/Groq/OpenRouter) directly with the user's key.
- Alternatives considered: A thin proxy backend (rejected: adds cost, privacy surface, and an account system); on-device browser AI only (rejected: too limited across providers — noted as a Proofly reference pattern).
- Consequences: Zero server cost and strong privacy story; shifts key handling and CORS entirely into the extension; no server-side rate limiting or abuse controls.
- Verification: Manual — confirm no network calls leave the extension except to the selected provider endpoint.
- Owner / date: Phase 1, 2026-03-06
D-2026-03-06-02 — Inline styles, no Tailwind
- Status: accepted
- Context: Content-script UI must not be broken by host-page CSS; WXT PostCSS/Tailwind integration was not wired.
- Decision: Style all UI with inline style objects (
Object.assign(el.style, …)/style={{…}}), using a dark Catppuccin-ish palette. - Alternatives considered: Tailwind (left in devDependencies but inactive); shadow DOM + stylesheet (deferred — see D-...-03).
- Consequences: Reliable rendering on any host page; palette/button styles get duplicated across content/Options/Popup (refactor tracked in TASKS #5).
- Verification: Visual check on multiple sites.
- Owner / date: Phase 1, 2026-03-06
D-2026-03-06-03 — No shadow DOM; data-lexai guard instead
- Status: accepted
- Context: Injected toolbar/modal could collide with host-page styles or re-trigger LexAI's own handlers.
- Decision: Inject directly into
document.body; mark every LexAI nodedata-lexai="true"and skip events whose target isclosest('[data-lexai="true"]'); use maxz-index(2147483647). - Alternatives considered: Shadow DOM (rejected for now: added complexity; revisit if style isolation issues appear).
- Consequences: Simple and working; weaker isolation than shadow DOM; highly customized editors (e.g. Google Docs) may not accept programmatic replace.
- Verification: Manual across textarea/input/contenteditable sites.
- Owner / date: Phase 1, 2026-03-06
D-2026-03-06-04 — Eager selection snapshot before async
- Status: accepted
- Context: Focus shifts to the toolbar and the live selection is gone by the time an async provider response returns.
- Decision: Capture the active element + selection offsets eagerly (on
mouseupand on buttonmousedown) and snapshot before anyawait; Replace uses the snapshot. Handle both textarea/input (selectionStart/End) and contenteditable/DOM (RangeAPI). - Alternatives considered: Re-reading selection after the response (rejected: selection no longer exists).
- Consequences: Replace works reliably; the pattern is fragile — editing content.ts must preserve snapshot-before-await. Zero automated coverage today (TASKS #10).
- Owner / date: Phase 1, 2026-03-06
D-2026-03-06-05 — Dual ANALYZE_TEXT message shapes; return true listener
- Status: accepted
- Context: Content script/popup send
{ payload: {...} }; other call sites send flat{ text, action, style }. Async responses need the message channel held open. - Decision: The handler accepts both shapes; the
onMessagelistener returnstrue.fixnormalizes togrammar. - Consequences: Flexible but must be preserved in both forms; removing
return truesilently breaks all responses. - Owner / date: Phase 1, 2026-03-06
D-2026-xx-xx-06 — tweetnacl secretbox for the API key (obfuscation, not protection)
- Status: accepted — flagged for revisit
- Context: Storing the raw key in
chrome.storage.locallooked bad; added tweetnaclsecretboxencryption (apiKeyEnc+encKey). - Decision: Prefer the encrypted path; keep plaintext
apiKeyas back-compat until a migration exists. - Known weakness:
encKeyis stored next toapiKeyEnc, so anyone who can read storage can decrypt. This is obfuscation, not protection (RECOMMENDATIONS #2). - Alternatives to consider: derive the key from
chrome.storage.session/ WebCrypto / a user passphrase; and be honest in the UI ("stored locally, obscured"). Tracked in TASKS #2. - Owner / date: post-Phase 1
D-2026-03-06-07 — Gitea CI + Chrome Web Store deploy
- Status: accepted
- Context: Project hosts CI on Gitea, not GitHub Actions.
- Decision:
.gitea/workflows/ci.yml(typecheck→test→build→zip to registry) anddeploy-chrome.yml(onv*.*.*tag → CWS). Telegram notifications. Version must match inpackage.jsonandwxt.config.ts. - Known weakness: workflows
git cloneinto/tmpand sethttp.sslVerify false(RECOMMENDATIONS #17). Revisit for speed/security. - Owner / date: Phase 1, 2026-03-06
Open / proposed
D-PROPOSED — Narrow host permissions from <all_urls>
- Status: proposed (decide before serious Web Store push)
- Context: Content script injects into every frame of every site, including banking/email/internal apps; also the #1 CWS review slowdown (RECOMMENDATIONS #1).
- Options:
activeTab+ on-demand injection, or a user-configurable allowlist. - Verification: confirm actions still work after narrowing; measure review outcome.