Spec and claude

This commit is contained in:
2026-09-06 04:41:40 +02:00
parent bc7c769802
commit 68686f7b18
2 changed files with 224 additions and 0 deletions
+140
View File
@@ -0,0 +1,140 @@
# Nexa — Frontend Browser Extension Spec
## 1. Overview
Nexa is a blockchain-powered trading companion that helps traders manage the emotional/behavioral side of trading rather than the trades themselves. Because trades are already visible on-chain, Nexa doesn't need the user to manually log anything — it observes wallet activity and reacts to it.
This spec covers **only the frontend**: an open-source browser extension. The backend (wallet/RPC listening, loss detection, auth) is a separate private service and is treated here as an external API the extension talks to.
**Target browser:** built and tested on Zen (Firefox fork), but must be cross-platform (Firefox + Chromium-based via WebExtensions API / manifest v3 where possible). Avoid browser-specific APIs unless behind a compatibility shim.
**Current milestone:** the "Lockout" feature — disabling quick-buy interactions on supported trading sites after a qualifying loss, with a visible reason/notification, built in a modular way so more sites and more intervention types can be added later.
## 2. Core Concept Recap (for context, not build scope)
- User sets their own loss thresholds while calm (Ulysses-contract style).
- Backend watches the user's wallet (Solana RPC / Helius) and detects when a threshold-qualifying loss occurs.
- Backend pushes a lock/unlock signal to the extension (WebSocket).
- Extension enforces the lock in the DOM of the trading site the user is currently on.
- Escalation model: first disable new-position buy actions only (sell/manage stays enabled); if the user tries to circumvent it, escalate to fully blocking the site.
## 3. Scope of This Spec: The Lockout Feature (v1)
### 3.1 Behavior
When the extension receives a "locked" state (from backend, or a mocked/local state for UI dev purposes):
1. **Detect** buy-action elements on the current page for the active site adapter (see Site Adapters below).
2. **Visually disable** each detected buy element — do not just hide it. The user should see that an action exists but is blocked, not that it vanished. Preferred treatment: blur + reduced opacity + `pointer-events: none`, with a small lock icon overlay on hover/tap.
3. **Intercept** any click that somehow still reaches the underlying element (defense in depth) and prevent default/propagation.
4. **Show a reason.** On lock activation, surface a non-intrusive extension notification/popup (e.g. toast anchored near the browser action icon, or an injected banner) stating why it's locked, e.g. "Locked: -$62 loss on SOL/PIXELCAT crossed your $50 threshold." Include a way to view details (opens extension popup with more info) but no way to silently dismiss-and-unlock from that toast — unlocking is a deliberate action.
5. **Sell/manage actions remain untouched** — only buy-side elements are targeted.
6. When state changes to "unlocked," reverse all DOM modifications cleanly (no leftover classes/overlays).
### 3.2 Modularity Requirement
This must be built so that:
- Adding a new supported site = adding a new "site adapter" module, not touching core logic.
- Adding a new intervention type (e.g. full-site block, delayed buy with countdown, confirmation modal) = adding a new "intervention strategy" module, not touching site adapters.
- The lock/unlock *decision* (when to lock, for how long, why) is fully decoupled from *how it's enforced on the page* and from *how it's detected on the page*.
Suggested architecture:
```
/src
/background - extension background/service worker: owns WebSocket connection to backend, holds current lock state, messages content scripts
/content-scripts
/adapters
axiom.ts - site-specific selectors + DOM strategy for axiom.trade
<future-site>.ts
adapter-interface.ts - shared type/interface all adapters implement
/interventions
blur-disable.ts - visual lock treatment (v1 default)
full-block.ts - escalation: full page block overlay (stub for v1, structure only)
intervention-interface.ts
observer.ts - generic mutation observer that watches for adapter-declared selectors appearing/disappearing (SPA-safe)
content-index.ts - wires adapter + intervention + background messages together
/popup - extension popup UI: current status, reason for lock, threshold settings link
/notifications - toast/banner component injected into page or shown via browser notification API
/shared
messaging.ts - typed message contracts between background <-> content scripts <-> popup
types.ts
manifest.json
```
### 3.3 Site Adapter: Axiom.trade (first target)
An adapter declares:
- `matches`: URL pattern(s) this adapter applies to.
- `findBuyElements()`: returns all current buy-action DOM nodes on the page.
- `getContainer(el)`: given a buy element, returns the stable ancestor container to apply lock styling to (so we style the whole control, not just an inner span).
Known selectors/patterns to encode (from live markup on axiom.trade):
**Quick Buy button** (small pill-style buy amount button, e.g. "0.04"):
```html
<div class="flex min-h-[28px] flex-1 cursor-pointer items-center justify-center rounded-full text-[14px] font-medium leading-[18px] outline-none border border-increase/50 text-increase hover:bg-increase/5 hover:text-increaseHover touch-callout-none select-none">0.04</div>
```
- Detection: the reliable anchor is the ancestor with class `buy-click-container` — walk up from any `.border-increase\/50.text-increase` candidate, or more robustly, query `.buy-click-container` directly and treat it (or its clickable child) as the target.
- The 4th ancestor being `buy-click-container` is a *current* DOM depth fact, not something to hardcode as "4 levels up" — instead use `closest('.buy-click-container')` so it survives markup depth changes.
**Noob Mode Buy button** (the big "Buy {token}" button):
```html
<div class="flex min-h-[4px] flex-1 flex-row items-center justify-center px-[16px] pb-[16px]">
<button type="button" class="bg-increase hover:bg-increase/90 rounded-8 flex max-h-[36px] min-h-[36px] flex-1 flex-row items-center justify-center rounded-full p-[4px] undefined">
<span class="text-[14px] font-bold leading-[18px] text-background">
<span class="text-[#090909]">Buy pixelcat</span>
</span>
</button>
</div>
```
- Detection: match `button` elements whose class list includes `bg-increase` and whose text content matches `/^Buy\s/i`. Do not hardcode the token name ("pixelcat") — match the "Buy " prefix pattern instead, since it changes per token page.
- Fallback/secondary check: the button's own class `bg-increase` (their "increase/green" action color) combined with being inside a quick-buy/trade panel region, to avoid accidentally matching unrelated green buttons elsewhere on the page.
**Note for Claude Code:** Axiom.trade is a React SPA — buy elements mount/unmount as the user navigates between tokens without full page reloads. Use a `MutationObserver` on a stable root (e.g. `document.body` or the main app root) rather than a one-time `querySelectorAll` on load, and re-apply/remove the lock treatment whenever matching elements appear.
### 3.4 Lock Enforcement — Visual Treatment (v1: `blur-disable`)
For each detected buy container:
- Apply a wrapper/overlay (don't mutate the site's own classes destructively — wrap or overlay so original markup can be restored exactly on unlock).
- Visual: `filter: blur(2-3px)`, reduced opacity (~0.5), cursor `not-allowed`.
- Overlay a small lock icon, centered, not blurred, so it reads clearly as "locked" rather than "broken."
- `pointer-events: none` on the original element or a transparent click-catcher div on top of it.
- Click-catcher (if used) triggers the same "show reason" notification on click/tap, so users who try to click get immediate feedback rather than nothing happening.
### 3.5 Notification/Reason UI
- Small, dismissible toast, injected into the page (top-right corner, non-blocking) or via the extension's own popup badge/notification.
- Content: short reason (e.g. threshold crossed, amount, token if relevant) + a "View details" affordance that opens the extension popup.
- Must not offer a one-click unlock from the toast itself.
- Extension popup (separate, already-scoped-elsewhere UI surface) shows: current lock status, reason, time locked, link to adjust thresholds (thresholds config itself is out of scope for this spec — just leave a hook/placeholder).
### 3.6 State Source for This Milestone
Since backend integration (WebSocket, RPC loss detection) is separate work:
- Build the content-script/background lock state as consuming a simple typed interface, e.g. `{ locked: boolean, reason?: string, scope: 'buy-only' | 'full-block' }`.
- For UI development purposes, wire this to a mock/dev toggle (e.g. via the extension popup, or a `chrome.storage`/`browser.storage` flag) so the lock/unlock visuals can be demoed and iterated on before the real backend WebSocket is wired in.
- The background script should have a clearly isolated function/module (`getLockState()`/`setLockState()`) that's the single seam where the future WebSocket client will plug in.
## 4. Non-Goals for This Milestone
- No real backend connection yet (mock state only).
- No cost-basis tracking logic (backend concern).
- No full-site-block implementation detail beyond a stub module (structure only, so it's easy to fill in later).
- No threshold-setting UI (placeholder link only).
- Only one site adapter (axiom.trade) needs to be functional; architecture just needs to make adding more trivial.
## 5. Deliverable for This Pass
A working browser extension (loadable unpacked in Zen/Firefox, and ideally Chromium) that:
1. On axiom.trade, detects quick-buy and noob-mode buy elements per section 3.3.
2. Applies the blur/disable visual treatment per 3.4 when a dev-mode "locked" toggle is on.
3. Shows a reason toast per 3.5 when lock activates, and on click of a locked element.
4. Cleanly reverses all changes when toggled back to unlocked.
5. Is structured per the modular architecture in 3.2 so a second site adapter and a second intervention type could be added without touching existing files beyond registration.
## 6. Repo / Licensing Context
- This frontend repo is the OSS component; description: "Your blockchain powered Agent to help with your trading emotions."
- License: Apache 2.0 (consistent with other OSS projects, and chosen partly to build trust given the extension interacts with a trading wallet).
- Backend stays private/closed-source.