diff --git a/README.md b/README.md index ab578dc..1a6fe1c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,42 @@ -# copilot -Your blockchain powered Agent to help with your trading emotions +# Osias — copilot + +Your blockchain-powered trading companion. This is the open-source browser extension +frontend (Apache License 2.0). See [`CLAUDE.md`](CLAUDE.md) for full architecture and +protocol documentation. + +## Build instructions + +Built with [WXT](https://wxt.dev) (Vite-based). To reproduce the exact `.output/firefox-mv3` +bundle submitted to addons.mozilla.org from source: + +**Environment** +- Node.js 22.x (built and tested on 22.11.0) +- npm 11.x (built and tested on 11.14.1) + +**Steps** +```sh +npm install # also runs `wxt prepare` via postinstall +npm run build:firefox +``` + +Output is written to `.output/firefox-mv3/`. To produce the uploaded `.zip` directly: + +```sh +npm run zip:firefox +``` + +Output zip is written to `.output/`. + +No build secrets, environment variables, or private dependencies are required — everything +needed to build is in this repository and on the public npm registry, pinned via +`package-lock.json`. + +## Development + +- `npm run dev:firefox` — dev build + watch targeting Firefox/Zen, auto-opens a temporary + profile with the extension loaded. +- `npm run compile` — `tsc --noEmit` type-check only. + +## License + +Apache License 2.0 — see [`LICENSE`](LICENSE). diff --git a/src/content-scripts/interventions/blur-disable.ts b/src/content-scripts/interventions/blur-disable.ts index aa80a90..d174daf 100644 --- a/src/content-scripts/interventions/blur-disable.ts +++ b/src/content-scripts/interventions/blur-disable.ts @@ -17,8 +17,33 @@ const locked = new WeakMap(); // reason toast is surfaced on `click` once the gesture completes. const INTERCEPTED_EVENT_TYPES = ['pointerdown', 'mousedown', 'mouseup', 'click'] as const; -const LOCK_ICON_SVG = - ''; +const SVG_NS = 'http://www.w3.org/2000/svg'; + +function createLockIcon(): SVGSVGElement { + const svg = document.createElementNS(SVG_NS, 'svg'); + svg.setAttribute('viewBox', '0 0 24 24'); + svg.setAttribute('width', '16'); + svg.setAttribute('height', '16'); + svg.setAttribute('fill', 'none'); + svg.setAttribute('stroke', 'currentColor'); + svg.setAttribute('stroke-width', '2'); + svg.setAttribute('stroke-linecap', 'round'); + svg.setAttribute('stroke-linejoin', 'round'); + + const rect = document.createElementNS(SVG_NS, 'rect'); + rect.setAttribute('x', '4'); + rect.setAttribute('y', '10'); + rect.setAttribute('width', '16'); + rect.setAttribute('height', '10'); + rect.setAttribute('rx', '2'); + svg.appendChild(rect); + + const path = document.createElementNS(SVG_NS, 'path'); + path.setAttribute('d', 'M8 10V7a4 4 0 0 1 8 0v3'); + svg.appendChild(path); + + return svg; +} // `backdrop-filter` (not `filter`) is deliberate: `filter` on the container // would recomposite the container's entire subtree — including the lock @@ -80,7 +105,7 @@ export const blurDisable: Intervention = { const overlay = document.createElement('div'); overlay.dataset.osiasOverlay = 'true'; overlay.style.cssText = OVERLAY_STYLE; - overlay.innerHTML = LOCK_ICON_SVG; + overlay.appendChild(createLockIcon()); const onOverlayEvent = (event: Event) => { event.preventDefault(); diff --git a/src/notifications/toast.ts b/src/notifications/toast.ts index 1e0d313..e6d0fe4 100644 --- a/src/notifications/toast.ts +++ b/src/notifications/toast.ts @@ -54,22 +54,35 @@ function ensureToast(): HTMLElement { el = document.createElement('div'); el.id = TOAST_ID; - el.innerHTML = ` -
🔒
-
-
Osias: buying locked
-
-
View details
-
- `; + + const icon = document.createElement('div'); + icon.className = 'osias-toast-icon'; + icon.textContent = '🔒'; + + const body = document.createElement('div'); + body.className = 'osias-toast-body'; + + const title = document.createElement('div'); + title.className = 'osias-toast-title'; + title.textContent = 'Osias: buying locked'; + + const reasonEl = document.createElement('div'); + reasonEl.className = 'osias-toast-reason'; + + const action = document.createElement('div'); + action.className = 'osias-toast-action'; + action.textContent = 'View details'; // No manual close button — the toast only goes away on its own (auto-hide // timer below) or when the lock state actually changes; clicking it never // unlocks anything. - el.querySelector('.osias-toast-action')?.addEventListener('click', () => { + action.addEventListener('click', () => { const message: OsiasMessage = { type: 'osias:open-popup' }; browser.runtime.sendMessage(message).catch(() => undefined); }); + body.append(title, reasonEl, action); + el.append(icon, body); + document.body.appendChild(el); return el; } diff --git a/wxt.config.ts b/wxt.config.ts index cb86627..19ecfaa 100644 --- a/wxt.config.ts +++ b/wxt.config.ts @@ -17,6 +17,17 @@ export default defineConfig({ gecko: { // Placeholder id for local/dev builds; replace before publishing to AMO. id: 'osias-extension@osias.trade', + // Firefox-required data collection disclosure (Firefox 140+ / AMO + // validation). Reflects what the extension actually transmits to the + // backend: wallet address + signature during Phantom sign-in + // (authenticationInfo, personallyIdentifyingInfo), and the wallet + // identity that ties the account to on-chain trading activity + // (financialAndPaymentInfo). See copilot/CLAUDE.md's "Backend + // connection" and "Wallet auth" sections. + data_collection_permissions: { + required: ['authenticationInfo', 'personallyIdentifyingInfo', 'financialAndPaymentInfo'], + optional: [], + }, }, }, },