diff --git a/.omni/e42f8a19-untitled/memory.md b/.omni/e42f8a19-untitled/memory.md
new file mode 100644
index 0000000..ef4f66c
--- /dev/null
+++ b/.omni/e42f8a19-untitled/memory.md
@@ -0,0 +1,23 @@
+# Workspace Context
+
+
+
+
+**Workspace root (absolute path):** `/home/workspaces/conversations/e42f8a19-5d65-46b6-a008-ae3e31a703c2`
+
+## Repositories
+
+- **`proofsnap-extension/`** — Branch: `omni/e42f8a19/proofsnap-extension`, Remote: `numbersprotocol/proofsnap-extension`
+ - Snap once. Prove forever. Turn your browser into a trust engine—sealing screenshots with cryptographic proof.
+
+## Environment & Tools
+
+- `proofsnap-extension/`: React 19 + TypeScript Chrome MV3 extension built with Vite; use `npm ci`, `npm run type-check`, `npm run lint`, and `npm run build`.
+- Build requires `manifest.json`; generate it from `manifest.template.json` with `OAUTH_CLIENT_ID` and `EXTENSION_KEY` before `npm run build`.
+
+## Key Discoveries
+
+- Selection capture is coordinated in `src/background/service-worker.ts`: popup sends `CAPTURE_SCREENSHOT`, service worker injects the selection overlay, then receives `SELECTION_COMPLETE` and crops via the offscreen document.
+
+---
+_Last system refresh: 2026-05-06 06:33 UTC_
diff --git a/src/background/service-worker.ts b/src/background/service-worker.ts
index 6b6f540..46fb494 100644
--- a/src/background/service-worker.ts
+++ b/src/background/service-worker.ts
@@ -223,6 +223,204 @@ function rejectPendingSelection(error: unknown): void {
resetPendingSelectionState();
}
+function getErrorMessage(error: unknown): string {
+ if (error instanceof Error) return error.message;
+ if (error && typeof error === 'object') {
+ const maybeMessage = (error as { message?: unknown }).message;
+ if (typeof maybeMessage === 'string') return maybeMessage;
+ }
+ return String(error);
+}
+
+function startProofSnapSelectionOverlay(): void {
+ if ((window as any).__proofSnapSelectionActive) {
+ return;
+ }
+
+ (window as any).__proofSnapSelectionActive = true;
+
+ type SelectionCoordinates = {
+ x: number;
+ y: number;
+ width: number;
+ height: number;
+ };
+
+ let overlay: HTMLDivElement | null = null;
+ let selectionBox: HTMLDivElement | null = null;
+ let isSelecting = false;
+ let startX = 0;
+ let startY = 0;
+
+ function cleanup(): void {
+ document.removeEventListener('mousemove', handleMouseMove);
+ document.removeEventListener('mouseup', handleMouseUp);
+ document.removeEventListener('keydown', handleKeyDown);
+
+ ['proofsnap-selection-overlay', 'proofsnap-selection-box', 'proofsnap-instructions'].forEach((id) => {
+ document.getElementById(id)?.remove();
+ });
+
+ overlay = null;
+ selectionBox = null;
+ (window as any).__proofSnapSelectionActive = false;
+ }
+
+ function sendResponse(data: any): void {
+ chrome.runtime.sendMessage({
+ type: 'SELECTION_COMPLETE',
+ payload: data,
+ });
+ }
+
+ function handleMouseDown(e: MouseEvent): void {
+ if (e.button !== 0) return;
+
+ isSelecting = true;
+ startX = e.clientX;
+ startY = e.clientY;
+
+ if (selectionBox) {
+ selectionBox.style.display = 'block';
+ selectionBox.style.left = `${startX}px`;
+ selectionBox.style.top = `${startY}px`;
+ selectionBox.style.width = '0px';
+ selectionBox.style.height = '0px';
+ }
+
+ if (overlay) {
+ overlay.style.background = 'transparent';
+ }
+ }
+
+ function handleMouseMove(e: MouseEvent): void {
+ if (!isSelecting || !selectionBox) return;
+
+ const currentX = e.clientX;
+ const currentY = e.clientY;
+ const left = Math.min(startX, currentX);
+ const top = Math.min(startY, currentY);
+ const width = Math.abs(currentX - startX);
+ const height = Math.abs(currentY - startY);
+
+ selectionBox.style.left = `${left}px`;
+ selectionBox.style.top = `${top}px`;
+ selectionBox.style.width = `${width}px`;
+ selectionBox.style.height = `${height}px`;
+ }
+
+ function handleMouseUp(e: MouseEvent): void {
+ if (!isSelecting) return;
+
+ isSelecting = false;
+
+ const currentX = e.clientX;
+ const currentY = e.clientY;
+ const left = Math.min(startX, currentX);
+ const top = Math.min(startY, currentY);
+ const width = Math.abs(currentX - startX);
+ const height = Math.abs(currentY - startY);
+
+ if (width < 10 || height < 10) {
+ cleanup();
+ sendResponse({ cancelled: true, reason: 'Selection too small' });
+ return;
+ }
+
+ const dpr = window.devicePixelRatio || 1;
+ const coordinates: SelectionCoordinates = {
+ x: Math.round(left * dpr),
+ y: Math.round(top * dpr),
+ width: Math.round(width * dpr),
+ height: Math.round(height * dpr),
+ };
+
+ cleanup();
+ sendResponse({
+ cancelled: false,
+ coordinates,
+ viewportCoordinates: { x: left, y: top, width, height },
+ });
+ }
+
+ function handleKeyDown(e: KeyboardEvent): void {
+ if (e.key === 'Escape') {
+ cleanup();
+ sendResponse({ cancelled: true, reason: 'User cancelled' });
+ }
+ }
+
+ function initSelectionOverlay(): void {
+ overlay = document.createElement('div');
+ overlay.id = 'proofsnap-selection-overlay';
+ overlay.style.cssText = `
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ background: rgba(0, 0, 0, 0.5);
+ z-index: 2147483647;
+ cursor: crosshair;
+ user-select: none;
+ `;
+
+ selectionBox = document.createElement('div');
+ selectionBox.id = 'proofsnap-selection-box';
+ selectionBox.style.cssText = `
+ position: fixed;
+ border: 2px dashed #fff;
+ background: transparent;
+ box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5);
+ z-index: 2147483647;
+ display: none;
+ pointer-events: none;
+ `;
+
+ const instructions = document.createElement('div');
+ instructions.id = 'proofsnap-instructions';
+ instructions.innerHTML = `
+
+ ProofSnap - Click and drag to select area. Press Esc to cancel.
+
+ `;
+
+ document.body.appendChild(overlay);
+ document.body.appendChild(selectionBox);
+ document.body.appendChild(instructions);
+
+ overlay.addEventListener('mousedown', handleMouseDown);
+ document.addEventListener('mousemove', handleMouseMove);
+ document.addEventListener('mouseup', handleMouseUp);
+ document.addEventListener('keydown', handleKeyDown);
+ }
+
+ if (document.body) {
+ initSelectionOverlay();
+ } else {
+ document.addEventListener('DOMContentLoaded', initSelectionOverlay, { once: true });
+ }
+}
+
/**
* Handle selection mode capture
* Injects content script and waits for user selection
@@ -246,13 +444,21 @@ async function handleSelectionCapture(tab: chrome.tabs.Tab, fromPopup: boolean):
// Inject the selection overlay content script
try {
+ try {
+ await chrome.windows.update(tab.windowId, { focused: true });
+ await chrome.tabs.update(tab.id, { active: true });
+ } catch (focusError) {
+ logger.warn('Could not focus selection target before injection:', getErrorMessage(focusError));
+ }
+
await chrome.scripting.executeScript({
target: { tabId: tab.id },
- files: ['content/selection-overlay.js'],
+ func: startProofSnapSelectionOverlay,
});
} catch (error) {
- logger.error('Failed to inject selection script:', error);
- throw new Error('Failed to start selection mode. Make sure you are on a valid web page.');
+ const errorMessage = getErrorMessage(error);
+ logger.error('Failed to inject selection script:', errorMessage);
+ throw new Error(`Failed to start selection mode: ${errorMessage}`);
}
const selectionTarget: PendingSelectionTarget = {
diff --git a/src/content/selection-overlay.ts b/src/content/selection-overlay.ts
deleted file mode 100644
index 1f65c6b..0000000
--- a/src/content/selection-overlay.ts
+++ /dev/null
@@ -1,224 +0,0 @@
-/**
- * Selection Overlay Content Script
- * Allows users to select a region of the page for screenshot capture
- */
-
-// Prevent multiple injections
-if (!(window as any).__proofSnapSelectionActive) {
- (window as any).__proofSnapSelectionActive = true;
-
- interface SelectionCoordinates {
- x: number;
- y: number;
- width: number;
- height: number;
- }
-
- let overlay: HTMLDivElement | null = null;
- let selectionBox: HTMLDivElement | null = null;
- let isSelecting = false;
- let startX = 0;
- let startY = 0;
-
- /**
- * Initialize the selection overlay
- */
- function initSelectionOverlay(): void {
- // Create dark overlay
- overlay = document.createElement('div');
- overlay.id = 'proofsnap-selection-overlay';
- overlay.style.cssText = `
- position: fixed;
- top: 0;
- left: 0;
- width: 100vw;
- height: 100vh;
- background: rgba(0, 0, 0, 0.5);
- z-index: 2147483647;
- cursor: crosshair;
- user-select: none;
- `;
-
- // Create selection box
- selectionBox = document.createElement('div');
- selectionBox.id = 'proofsnap-selection-box';
- selectionBox.style.cssText = `
- position: fixed;
- border: 2px dashed #fff;
- background: transparent;
- box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5);
- z-index: 2147483647;
- display: none;
- pointer-events: none;
- `;
-
- // Create instructions tooltip
- const instructions = document.createElement('div');
- instructions.id = 'proofsnap-instructions';
- instructions.innerHTML = `
-
- ProofSnap - Click and drag to select area. Press Esc to cancel.
-
- `;
-
- document.body.appendChild(overlay);
- document.body.appendChild(selectionBox);
- document.body.appendChild(instructions);
-
- // Add event listeners
- overlay.addEventListener('mousedown', handleMouseDown);
- document.addEventListener('mousemove', handleMouseMove);
- document.addEventListener('mouseup', handleMouseUp);
- document.addEventListener('keydown', handleKeyDown);
- }
-
- /**
- * Handle mouse down - start selection
- */
- function handleMouseDown(e: MouseEvent): void {
- if (e.button !== 0) return; // Only left click
-
- isSelecting = true;
- startX = e.clientX;
- startY = e.clientY;
-
- if (selectionBox) {
- selectionBox.style.display = 'block';
- selectionBox.style.left = `${startX}px`;
- selectionBox.style.top = `${startY}px`;
- selectionBox.style.width = '0px';
- selectionBox.style.height = '0px';
- }
-
- // Hide overlay background while selecting (selection box has its own shadow)
- if (overlay) {
- overlay.style.background = 'transparent';
- }
- }
-
- /**
- * Handle mouse move - update selection box
- */
- function handleMouseMove(e: MouseEvent): void {
- if (!isSelecting || !selectionBox) return;
-
- const currentX = e.clientX;
- const currentY = e.clientY;
-
- const left = Math.min(startX, currentX);
- const top = Math.min(startY, currentY);
- const width = Math.abs(currentX - startX);
- const height = Math.abs(currentY - startY);
-
- selectionBox.style.left = `${left}px`;
- selectionBox.style.top = `${top}px`;
- selectionBox.style.width = `${width}px`;
- selectionBox.style.height = `${height}px`;
- }
-
- /**
- * Handle mouse up - complete selection
- */
- function handleMouseUp(e: MouseEvent): void {
- if (!isSelecting) return;
-
- isSelecting = false;
-
- const currentX = e.clientX;
- const currentY = e.clientY;
-
- const left = Math.min(startX, currentX);
- const top = Math.min(startY, currentY);
- const width = Math.abs(currentX - startX);
- const height = Math.abs(currentY - startY);
-
- // Minimum selection size
- if (width < 10 || height < 10) {
- cleanup();
- sendResponse({ cancelled: true, reason: 'Selection too small' });
- return;
- }
-
- // Calculate coordinates relative to page (accounting for scroll and device pixel ratio)
- const dpr = window.devicePixelRatio || 1;
- const coordinates: SelectionCoordinates = {
- x: Math.round(left * dpr),
- y: Math.round(top * dpr),
- width: Math.round(width * dpr),
- height: Math.round(height * dpr),
- };
-
- cleanup();
- sendResponse({
- cancelled: false,
- coordinates,
- viewportCoordinates: { x: left, y: top, width, height }
- });
- }
-
- /**
- * Handle key down - cancel on Escape
- */
- function handleKeyDown(e: KeyboardEvent): void {
- if (e.key === 'Escape') {
- cleanup();
- sendResponse({ cancelled: true, reason: 'User cancelled' });
- }
- }
-
- /**
- * Send response back to service worker
- */
- function sendResponse(data: any): void {
- chrome.runtime.sendMessage({
- type: 'SELECTION_COMPLETE',
- payload: data,
- });
- }
-
- /**
- * Cleanup overlay elements
- */
- function cleanup(): void {
- document.removeEventListener('mousemove', handleMouseMove);
- document.removeEventListener('mouseup', handleMouseUp);
- document.removeEventListener('keydown', handleKeyDown);
-
- const elements = [
- 'proofsnap-selection-overlay',
- 'proofsnap-selection-box',
- 'proofsnap-instructions',
- ];
-
- elements.forEach((id) => {
- const el = document.getElementById(id);
- if (el) el.remove();
- });
-
- overlay = null;
- selectionBox = null;
- (window as any).__proofSnapSelectionActive = false;
- }
-
- // Initialize on load
- initSelectionOverlay();
-}
diff --git a/vite.config.ts b/vite.config.ts
index b5839c4..279ae10 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -41,8 +41,7 @@ export default defineConfig({
options: resolve(__dirname, 'src/options/options.html'),
background: resolve(__dirname, 'src/background/service-worker.ts'),
offscreen: resolve(__dirname, 'src/offscreen/offscreen.ts'),
- share: resolve(__dirname, 'src/share/share.tsx'),
- 'content/selection-overlay': resolve(__dirname, 'src/content/selection-overlay.ts')
+ share: resolve(__dirname, 'src/share/share.tsx')
},
output: {
entryFileNames: '[name].js',