Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Context: Content Script & Background Worker

## Project Structure
- Extension: Vanilla JS, Manifest V3, no build system
- Files to implement: `extension/content/extract.js`, `extension/background/worker.js`
- Dependency: `extension/utils/browser-compat.js` (already implemented - provides `browserAPI` global)

## Key Patterns
- `browserAPI` is a global IIFE that normalizes Firefox `browser.*` and Chrome `chrome.*`
- Available APIs: `runtime.sendMessage`, `runtime.onMessage`, `storage.local.get/set`, `tabs.query`, `scripting.executeScript`
- Manifest V3 service worker pattern (no persistent background page)
- Content script registered in manifest with `"run_at": "document_idle"`
- `browser-compat.js` is NOT imported via ES modules - it's a script tag in popup.html but for content/background it would need `importScripts` or manifest injection

## API Contract
- POST /api/v1/analyze: `{ url: string, html: string }` -> `AnalysisResult`
- AnalysisResult: `{ url, overall_score, risk_level, dimensions, key_concerns, summary, cached, analyzed_at }`
- Error: `{ error: string }` with HTTP 4xx/5xx
- Timeout: 60s (504 from backend)

## Messaging Protocol (from task spec)
- Popup -> Background: `{ type: "analyze" }`
- Background -> Content: `{ type: "extract" }`
- Content -> Background: `{ type: "extracted", html: "...", url: "..." }`
- Background -> Popup: `{ type: "result", data: {...} }` or `{ type: "error", message: "..." }`

## Content Extraction Rules
- Strip: `<script>`, `<style>`, `<nav>`, `<footer>`, `<header>`, `<noscript>`, `<iframe>`
- Strip sidebar patterns (common class/role attributes)
- Prefer: `<main>`, `<article>`, `[role="main"]` as root
- Fallback: `<body>` (with stripping applied)
- Return cleaned HTML string (backend handles further parsing)

## Important Notes
- Content script has access to page DOM directly
- Background worker communicates with content script via messaging
- Background worker can also use `scripting.executeScript` to inject into tabs
- The content script is injected on all URLs per manifest
- `browser-compat.js` needs to be available in both content and background contexts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Plan: Content Script & Background Worker

## Test Strategy

Per CLAUDE.md and the task spec, extension testing is manual only for MVP:
> "Extension: Manual testing only for MVP. Load unpacked in Firefox + Chrome, test on real sites."

No test framework (Jest, Vitest, etc.) exists in the project, and no package.json is present. The task spec's acceptance criteria are verified by manual testing:
1. Load extension unpacked in browser
2. Navigate to a privacy policy page
3. Click extension icon -> verify content extraction, API call, and response flow

Code will be written with clean separation of concerns to facilitate future testing if needed.

## Implementation Plan

### Phase 1: Content Script (`content/extract.js`)

**Functions to implement:**
1. `extractMainContent()` - Main orchestrator
- Find best content root: `<main>` > `<article>` > `[role="main"]` > `<body>`
- Clone the root element (to avoid mutating the live DOM)
- Strip unwanted elements from clone
- Return the innerHTML of the cleaned clone

2. `findContentRoot()` - Element selection heuristic
- Check for `<main>`, `<article>`, `[role="main"]` in priority order
- Fall back to `document.body`

3. `stripUnwantedElements(root)` - DOM cleanup
- Remove elements matching tags: script, style, nav, footer, header, noscript, iframe
- Remove sidebar patterns: `[role="complementary"]`, `[aria-label*="sidebar"]`, common class names (sidebar, side-bar, aside)
- Remove `<aside>` elements

4. Message listener
- Listen for `{ type: "extract" }` from background
- Respond with `{ type: "extracted", html, url }`

### Phase 2: Background Worker (`background/worker.js`)

**Functions to implement:**
1. `getApiUrl()` - Get configured API URL from storage
- Check `storage.local` for `apiUrl`
- Default to `http://localhost:8080`

2. `extractFromTab(tabId)` - Trigger content script extraction
- Send `{ type: "extract" }` message to content script in specified tab
- Return the extracted HTML and URL

3. `analyzeContent(url, html)` - POST to backend API
- Fetch `POST {apiUrl}/api/v1/analyze` with `{ url, html }`
- Handle timeout (60s via AbortController)
- Handle network errors, HTTP errors
- Return parsed response or error

4. `handleAnalyzeMessage(sender)` - Main orchestrator
- Get active tab
- Extract content via messaging
- Send to API
- Return result or error

5. Message listener
- Listen for `{ type: "analyze" }` from popup
- Call handleAnalyzeMessage and respond

### Messaging Flow
```
Popup sends: { type: "analyze" }
-> Background receives, gets active tab
-> Background sends to tab: { type: "extract" }
-> Content script receives, extracts HTML
-> Content script responds: { type: "extracted", html, url }
-> Background receives HTML
-> Background POSTs to API: { url, html }
-> API responds with AnalysisResult
-> Background responds to popup: { type: "result", data: AnalysisResult }
(or on error): { type: "error", message: "..." }
```

### Key Decisions
1. **Content script uses message listener** (not scripting.executeScript) since it's already injected via manifest
2. **Use `sendResponse` with `return true`** for async responses in message handlers
3. **Clone DOM before stripping** to avoid mutating the live page
4. **AbortController for fetch timeout** - 60s to match backend timeout
5. **browser-compat.js imported via importScripts** in background worker (Manifest V3 service worker)
6. **Content script** - browser-compat.js must be listed before extract.js in manifest content_scripts

## Checklist
- [ ] Implement content/extract.js with extraction logic
- [ ] Implement content/extract.js message listener
- [ ] Implement background/worker.js getApiUrl
- [ ] Implement background/worker.js content extraction trigger
- [ ] Implement background/worker.js API communication
- [ ] Implement background/worker.js message handler
- [ ] Update manifest.json to include browser-compat.js in content scripts
- [ ] Verify end-to-end message flow works conceptually
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Progress: Content Script & Background Worker

## Setup
- [x] Created documentation directory
- [x] Discovered instruction files (CLAUDE.md, backend/README.md)
- [x] Read design doc and task spec
- [x] Created context.md

## Explore
- [x] Analyzed requirements
- [x] Read existing extension files (manifest, browser-compat, popup stubs)
- [x] Researched backend API types (AnalysisRequest, AnalysisResult)
- [x] Identified messaging protocol

## Plan
- [x] Design test strategy (manual testing per project convention)
- [x] Create implementation plan (plan.md)

## Code
- [x] Implement content/extract.js — content extraction with DOM cloning and element stripping
- [x] Implement background/worker.js — orchestration, API communication, error handling
- [x] Update manifest.json — added browser-compat.js to content_scripts
- [x] Add tabs.sendMessage to browser-compat.js — needed for background->content messaging
- [x] Refactor analyzeContent() — separated fetch errors from HTTP errors for cleaner error handling

## Decisions
- Content script uses synchronous sendResponse (DOM extraction is fast)
- Background worker uses async sendResponse with `return true`
- DOM is cloned before stripping to avoid mutating the live page
- Single article heuristic: only use `<article>` as root if there's exactly one
- 60s fetch timeout via AbortController matches backend timeout

## Commit
- [ ] Create conventional commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Context: Extension Scaffold and Manifest

## Requirements

1. Create full `extension/` directory structure with all required files
2. Create valid Manifest V3 `manifest.json` (Firefox + Chrome compatible)
3. Create placeholder icon PNGs at 16x16, 48x48, 128x128
4. Create `utils/browser-compat.js` normalizing `browser.*`/`chrome.*` APIs
5. Create placeholder files for background worker, content script, and popup

## Key Design Decisions

- **No build system**: Vanilla JS loaded directly
- **Manifest V3**: Current standard for both Firefox and Chrome
- **Minimal permissions**: `activeTab` and `storage` only
- **Browser compat polyfill**: Wraps Promise-based (`browser.*`) vs callback-based (`chrome.*`) differences
- **APIs to normalize**: `runtime.sendMessage`, `runtime.onMessage`, `storage.local.get/set`, `tabs.query`, `scripting.executeScript`

## Existing Patterns

- Project uses Go backend with existing `backend/` directory
- Extension is a separate top-level `extension/` directory
- No CODEASSIST.md found

## Implementation Path

All files go under `extension/` at the repo root. No dependencies on backend code.
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Plan: Extension Scaffold and Manifest

## Testing Strategy

Per the design doc, extension testing is manual only for MVP:
- Load unpacked in Firefox and Chrome
- Verify no console errors
- Verify manifest loads without warnings

No automated tests for this task.

## Implementation Plan

### 1. Create directory structure
Create `extension/` with subdirectories: `background/`, `content/`, `popup/`, `icons/`, `utils/`

### 2. Create manifest.json
Manifest V3 with:
- `manifest_version: 3`
- Permissions: `activeTab`, `storage`
- Background service worker
- Content scripts (all URLs, document_idle)
- Browser action with popup and icons
- `browser_specific_settings` for Firefox compatibility (gecko ID)

### 3. Generate placeholder icons
Use Python/ImageMagick to create simple solid-color PNGs at 16x16, 48x48, 128x128

### 4. Create browser-compat.js
Normalize `browser.*` vs `chrome.*`:
- Detect available API namespace
- For Chrome: wrap callback-based APIs in Promises
- For Firefox: use native `browser.*` directly
- Export unified API object

### 5. Create placeholder files
Minimal boilerplate with comments for:
- `background/worker.js`
- `content/extract.js`
- `popup/popup.html`, `popup/popup.js`, `popup/popup.css`
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Progress: Extension Scaffold and Manifest

## Setup
- [x] Created documentation directory structure
- [x] Discovered instruction files (README.md in backend/)
- [x] Read detailed design document
- [x] Created context.md

## Implementation Checklist
- [x] Create directory structure (`extension/` with subdirectories)
- [x] Create `manifest.json` (Manifest V3, Firefox + Chrome)
- [x] Create placeholder icon PNGs (16x16, 48x48, 128x128)
- [x] Create `utils/browser-compat.js` (API normalization polyfill)
- [x] Create `background/worker.js` (placeholder)
- [x] Create `content/extract.js` (placeholder)
- [x] Create `popup/popup.html` (placeholder)
- [x] Create `popup/popup.js` (placeholder)
- [x] Create `popup/popup.css` (placeholder)
- [x] Verify manifest is valid JSON
- [x] Verify icons are valid PNGs at correct sizes

## Validation
- Manifest validates as JSON with correct manifest_version (3), permissions (activeTab, storage), service worker, and content scripts
- Icons are valid PNGs at 16x16, 48x48, and 128x128
- All 10 files present in correct directory structure
- No automated tests (extension uses manual testing per design doc)

## Notes
- Used teal/green (#2d9c6f) for placeholder icon color
- Firefox compat via `browser_specific_settings.gecko` with strict_min_version 109.0 (first Firefox version with full MV3 support)
- Browser compat polyfill wraps: runtime.sendMessage, runtime.onMessage, storage.local.get/set, tabs.query, scripting.executeScript
41 changes: 41 additions & 0 deletions .agents/scratchpad/2026-02-15-smolterms/popup-ui/context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Context: Popup UI Implementation

## Requirements
- Build extension popup displaying analysis results with loading, results, error, and not-a-policy states
- Fixed width ~360px, auto-height
- Pastel off-white color scheme, simple and readable
- Plain CSS (no Tailwind CDN) with clean Tailwind-inspired design
- Color-coded risk levels: Low (green), Moderate (yellow), High (orange), Critical (red)
- Communication via `browserAPI.runtime.sendMessage({ type: "analyze" })`

## Dependencies
- `extension/utils/browser-compat.js` - unified browser API layer
- `extension/background/worker.js` - handles analysis, responds with `{ type: "result", data }` or `{ type: "error", message }`

## API Response Format
```json
{
"url": "...",
"overall_score": 6.4,
"risk_level": "moderate",
"dimensions": { "data_collection": { "score": 7.0, "summary": "..." }, ... },
"key_concerns": ["...", "..."],
"summary": "...",
"cached": false,
"analyzed_at": "..."
}
```

## Risk Levels
| Range | Level | Color |
|-------|-------|-------|
| 8-10 | Low | Green (pastel) |
| 5-7.9 | Moderate | Yellow/Amber (pastel) |
| 3-4.9 | High | Orange (pastel) |
| 1-2.9 | Critical | Red (pastel) |

## Design Decisions
- Plain CSS over Tailwind CDN: avoids CSP issues, works offline, smaller footprint
- Off-white pastel theme for a soft, readable aesthetic
- CSS custom properties for theme colors (easy to modify)
- Dimension names mapped from snake_case to human-readable labels
43 changes: 43 additions & 0 deletions .agents/scratchpad/2026-02-15-smolterms/popup-ui/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Plan: Popup UI Implementation

## Test Strategy
This is a browser extension popup UI (HTML/CSS/JS). No automated test framework applies - manual testing per task spec.

### Manual Test Scenarios
1. Loading state: open popup -> see spinner and "Analyzing..." text
2. Results state: mock successful response -> overall score with correct risk color, 5 dimension scores, concerns list, summary
3. Error state: mock error response -> error message with retry button, retry re-triggers analysis
4. Not-a-policy state: mock not_policy response -> friendly informational message
5. Cached indicator: mock cached response -> subtle "cached" badge visible
6. Risk color coding: verify green (8-10), yellow (5-7.9), orange (3-4.9), red (1-2.9)
7. Popup size: fixed width ~360px, content doesn't overflow

## Implementation Plan

### 1. popup.html
- Clean semantic HTML with sections for each state (loading, results, error, not-policy)
- All states hidden by default, toggled via JS class
- Include browser-compat.js and popup.js scripts

### 2. popup.css
- CSS custom properties for pastel off-white theme
- Off-white background (#faf9f7 or similar)
- Pastel risk level colors
- Loading spinner keyframe animation
- Smooth state transitions
- Score bar visualization for dimensions
- Compact, scannable layout

### 3. popup.js
- On popup open: send `{ type: "analyze" }` to background worker
- State management: show/hide sections based on response
- Render functions: renderLoading(), renderResults(data), renderError(message), renderNotPolicy()
- Risk level mapping: score -> color/label
- Dimension label mapping: snake_case -> human readable
- Retry button handler

## Implementation Checklist
- [ ] popup.html - all UI states
- [ ] popup.css - theme, risk colors, animations, layout
- [ ] popup.js - messaging, state management, rendering
- [ ] Manual validation of all states
35 changes: 35 additions & 0 deletions .agents/scratchpad/2026-02-15-smolterms/popup-ui/progress.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Progress: Popup UI Implementation

## Setup
- [x] Created documentation directory
- [x] Explored codebase and existing files
- [x] Researched Tailwind CDN - decided on plain CSS for reliability (CSP issues, offline support)
- [x] Created context.md and plan.md

## Implementation
- [x] popup.html - semantic HTML with 4 states: loading, results, error, not-policy
- [x] popup.css - pastel off-white theme (#faf8f5), CSS custom properties, risk color coding, spinner animation
- [x] popup.js - messaging via browserAPI, state management, all render functions, retry handler
- [x] Refactor - fixed duplicate CSS properties in score-circle, verified conventions match worker.js/extract.js
- [x] Manual validation - all 9 acceptance criteria verified against implementation
- [x] Commit

## Design Decisions
- Plain CSS over Tailwind CDN: avoids CSP issues, works offline, no external dependency
- Off-white background (#faf8f5) with warm neutral palette
- Pastel risk colors with matching text/background variants
- Score displayed as circle with colored border
- Dimension bars with color-coded fill + numeric score
- Concerns displayed in warm orange background cards
- XSS prevention via escapeHtml() for dynamic content in innerHTML

## Acceptance Criteria Mapping
1. Loading state: spinner + "Analyzing..." + hint text
2. Results: score circle + risk badge + dimensions + concerns + summary
3. Risk colors: green (8-10), yellow (5-7.9), orange (3-4.9), red (1-2.9)
4. Dimensions: 5 rows with label, bar, score
5. Key concerns: bullet list in orange cards
6. Error + retry: message + "Try Again" button -> re-triggers triggerAnalysis()
7. Not-a-policy: info icon + friendly message
8. Cached: subtle "Cached" badge next to risk badge
9. Size: 380px width, auto height
Loading