- Prompt Builder: 12 named patterns (Direct/Reasoning/Agentic + Auto), live per-pattern hints in popup and in-page dialog, legacy promptStyle migration, and a 2048-token floor fix so longer patterns stop getting truncated. - CHANGELOG.md added (Keep a Changelog + SemVer); release.yml now builds the Gitea release body from the matching version section, with a generic fallback, plus a zip-root fix so manifest.json sits at the archive root. - package.json/package-lock.json bumped to 1.1.0; CLAUDE.md documents the CHANGELOG-gated release process.
6.9 KiB
6.9 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
D-2026-08-12-08 — CHANGELOG.md drives Gitea release notes
- Status: accepted
- Context: Releases previously shipped a hardcoded release body (
## LexAI ${VERSION}+ generic install steps) that never said what actually changed in that version. - Decision: Release notes live in
CHANGELOG.md(Keep a Changelog format, Semantic Versioning)..gitea/workflows/release.ymlextracts the section matching the pushed tag's version and uses it as the Gitea release body, with a generic fallback if no matching section exists. A version bump is not considered done untilCHANGELOG.mdhas that version's section. - Alternatives considered: Auto-generating notes from commit messages (rejected: commit history is not curated for user-facing wording); keeping the hardcoded body (rejected: uninformative to installers).
- Consequences: Every version bump now requires a
CHANGELOG.mdentry alongside thepackage.jsonbump; the release workflow degrades gracefully (generic body + logged warning) if that entry is missed rather than failing the release. - Verification:
release.ymlreviewed by an independent critic; P2 findings fixed. Confirmed locally that the section-extraction logic matches## [1.1.0]and stops at the next## [heading. - Owner / date: 2026-08-12
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.