5.7 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Repository state
This repo currently contains only a spec (NEXA_SPEC.md), README.md, and LICENSE — no source code, package.json, or manifest.json exist yet. There are no build/lint/test commands to run because nothing has been scaffolded. When implementation begins, this file should be updated with the actual commands (build, lint, test, load-unpacked instructions) once a package manager and bundler are chosen.
What this project is
Nexa is a blockchain-powered trading companion focused on the emotional/behavioral side of trading, not the trades themselves. Trades are already visible on-chain, so Nexa observes wallet activity rather than requiring manual logging. This repo is the open-source frontend: a browser extension. The backend (wallet/RPC listening via Solana/Helius, loss detection, auth, WebSocket push) is a separate private service and is out of scope here — treat it as an external API.
License: Apache 2.0. The backend stays closed-source; this frontend repo is the OSS component.
Full behavioral spec lives in NEXA_SPEC.md — read it before implementing the lockout feature or any site adapter/intervention. Key points below are a summary, not a replacement.
Target platform
- Built and tested primarily on Zen (Firefox fork), must also work cross-platform on Firefox and Chromium-based browsers (WebExtensions API / Manifest V3 where possible).
- Avoid browser-specific APIs unless behind a compatibility shim.
Current milestone: "Lockout" feature
When the extension receives a "locked" state, it visually disables (not hides) buy-action elements on supported trading sites, shows a non-dismissible-to-unlock reason toast, and intercepts clicks that reach the underlying element. Sell/manage actions are never touched. Unlocking reverses all DOM changes cleanly with no leftover overlays/classes.
Escalation model: first disable new-position buy actions only; if circumvented, escalate to a full-site block (stub only for v1).
Required architecture (modularity is the core constraint)
This is the most important design rule in the spec: lock/unlock decision-making, site-specific detection, and enforcement/intervention must be three fully decoupled concerns.
- Adding a new supported site = a new site adapter module, without touching core logic.
- Adding a new intervention type (full-site block, delayed buy with countdown, confirmation modal) = a new intervention strategy module, without touching adapters.
- The decision of when/how long/why to lock is decoupled from how it's enforced on the page and from how it's detected on the page.
Planned structure (from NEXA_SPEC.md §3.2):
/src
/background - 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
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)
intervention-interface.ts
observer.ts - generic MutationObserver watching for adapter-declared selectors (SPA-safe)
content-index.ts - wires adapter + intervention + background messages together
/popup - status, reason for lock, threshold settings link
/notifications - toast/banner injected into page or via browser notification API
/shared
messaging.ts - typed message contracts between background <-> content scripts <-> popup
types.ts
manifest.json
A site adapter's contract: matches (URL pattern), findBuyElements(), getContainer(el) (stable ancestor to style, so the whole control is styled rather than an inner span).
Axiom.trade adapter specifics (first target site)
- Axiom is a React SPA — buy elements mount/unmount without full page reloads. Use a
MutationObserveron a stable root (e.g.document.body), not a one-timequerySelectorAllon load. - Quick Buy button: find via
closest('.buy-click-container'), not a hardcoded ancestor depth (DOM depth is not stable). - Noob Mode Buy button: match
buttonelements with classbg-increasewhose text matches/^Buy\s/i. Do not hardcode token names — they vary per token page. - Full selector details and example markup are in
NEXA_SPEC.md§3.3.
Lock state contract
The lock/unlock state is a simple typed interface: { locked: boolean, reason?: string, scope: 'buy-only' | 'full-block' }. For v1 (no real backend yet), this is driven by a mock/dev toggle (e.g. via popup or browser.storage). The background script must expose a single isolated seam (getLockState() / setLockState()) where the real WebSocket client will later plug in — do not scatter lock-state reads/writes across modules.
Visual treatment for v1 (blur-disable)
- Wrap or overlay the original element rather than mutating its classes destructively, so the original markup can be restored exactly on unlock.
filter: blur(2-3px), ~0.5 opacity,cursor: not-allowed, a non-blurred centered lock icon overlay, andpointer-events: none(or a transparent click-catcher div).- A click on a locked element (or its click-catcher) must trigger the same reason toast as lock activation.
Non-goals for this milestone
- No real backend/WebSocket connection (mock state only).
- No cost-basis tracking logic (backend concern).
- No full-site-block implementation beyond a stub module.
- No threshold-setting UI (placeholder link only).
- Only the axiom.trade adapter needs to be functional; the architecture just needs to make adding more sites trivial.