ARGUS — implementation plan

Phased build plan for the capture extension and server.

ARGUS — Build Plan (Updated)

Argument Research & Graph Utility System


Target Platform

Edge-primary, Chrome-compatible. Edge is Chromium-based — all chrome.* APIs including chrome.sidePanel work identically. Load at edge://extensions. No Firefox support (Firefox lacks sidePanel and uses browser.* APIs — would require a separate code path).


Updated Data Model

Capture Object (with source location)

{
  "id": "cap_20260429_143200_001",
  "text": "Selected text content here",
  "weight": "green",
  "theme": "Climate Policy",
  "subtopic": "Carbon Tax",
  "source_url": "https://example.com/article",
  "source_title": "Article Page Title",
  "source_type": "webpage",
  "source_location": {
    "scroll_y": 1240,
    "anchor": "#methodology",
    "context_before": "...~100 chars before the selected text...",
    "context_after": "...~100 chars after the selected text...",
    "element_selector": "article > section:nth-child(3) > p:nth-child(2)"
  },
  "timestamp": "2026-04-29T14:32:00Z",
  "connections": [],
  "flagged": false
}

For PDFs: "source_type": "pdf" and "source_location": { "page": 12 }.

PDF page detection strategy: 1. Check if URL ends in .pdf 2. Extract #page=N from window.location.hash if present 3. If page number cannot be determined, show HUD input field for manual entry before committing capture

Connection Object

{
  "from_id": "cap_20260429_143200_001",
  "to_id": "cap_20260429_143500_003",
  "relationship": "contradicts",
  "created_at": "2026-04-29T15:00:00Z"
}

Relationship values: "supports" | "contradicts" | "extends" | "see also" | "custom"

Session File

{
  "session_id": "session_20260429",
  "created_at": "2026-04-29T12:00:00Z",
  "last_updated": "2026-04-29T16:00:00Z",
  "themes": [
    {
      "id": "theme_001",
      "name": "Climate Policy",
      "subtopics": ["Carbon Tax", "Emissions Trading", "Green New Deal"]
    }
  ],
  "captures": [],
  "connections": []
}

Export JSON (for LLM)

{
  "meta": {
    "export_version": "1.0",
    "exported_at": "2026-04-29T16:00:00Z",
    "session_id": "session_20260429",
    "total_captures": 34,
    "total_connections": 12,
    "themes": ["Climate Policy", "Economic Impact"],
    "instructions": "Green = supporting argument, orange = counter-argument, red = strong objection. Connections describe relationships. Synthesise by theme and subtopic."
  },
  "themes": [],
  "captures": [],
  "connections": []
}

Repository Structure

argus/
├── config.json                  ← shared config (port, keybinds, defaults)
├── ARGUS_PLAN.md
├── argus_spec.md
├── extension/
│   ├── manifest.json            ← MV3
│   ├── background.js            ← service worker: WS client, state relay
│   ├── content.js               ← injected: keyboard events, text grab, HUD toast
│   ├── panel/
│   │   ├── panel.html
│   │   ├── main.jsx             ← React entry point
│   │   ├── store.js             ← useReducer + Context
│   │   ├── ws.js                ← panel ↔ background messaging
│   │   ├── components/
│   │   │   ├── FeedView.jsx
│   │   │   ├── FeedCard.jsx
│   │   │   ├── GraphView.jsx
│   │   │   ├── ThemeNav.jsx
│   │   │   ├── ConnectionPopover.jsx
│   │   │   └── ReviewOverlay.jsx
│   │   └── panel.css
│   ├── vite.config.js
│   └── package.json
└── server/
    ├── main.py
    ├── websocket_server.py
    ├── state_manager.py
    ├── file_io.py
    ├── keybind_listener.py
    ├── config.py
    ├── requirements.txt
    └── data/
        └── sessions/

WebSocket Protocol

Extension → Server

{ "event": "CAPTURE", "data": { ...capture object } }
{ "event": "NAV", "action": "PREV_THEME" | "NEXT_THEME" | "CYCLE_SUBTOPIC" }
{ "event": "UNDO_LAST" }
{ "event": "REVIEW_LAST" }
{ "event": "GET_STATE" }
{ "event": "ADD_THEME", "data": { "name": "New Theme" } }
{ "event": "ADD_SUBTOPIC", "data": { "theme_id": "theme_001", "name": "New Subtopic" } }
{ "event": "ADD_CONNECTION", "data": { "from_id": "...", "to_id": "...", "relationship": "..." } }
{ "event": "EXPORT" }

Server → Extension

{ "event": "STATE", "data": { "theme": "...", "subtopic": "...", "captures": [...], "connections": [...] } }
{ "event": "CAPTURE_ACK", "data": { "id": "cap_..." } }
{ "event": "REVIEW_DATA", "data": { ...capture object } }
{ "event": "PROMPT_NEW_SUBTOPIC" }
{ "event": "EXPORT_DATA", "data": { ...full session with meta } }
{ "event": "ERROR", "message": "..." }

Extension ↔ Background Message Protocol

// content.js → background.js
{ type: "CAPTURE", payload: { text, url, title, weight, source_type, source_location } }
{ type: "NAV", action: "PREV_THEME" | "NEXT_THEME" | "CYCLE_SUBTOPIC" }
{ type: "REVIEW_LAST" }
{ type: "TOGGLE_PANEL" }
{ type: "PDF_PAGE_CONFIRMED", payload: { page: 12 } }

// background.js → content.js (state broadcast)
{ type: "STATE_UPDATE", state: { theme, subtopic, lastCapture } }
{ type: "SHOW_REVIEW", data: { ...capture } }
{ type: "PROMPT_NEW_SUBTOPIC" }

Keybind Mapping (config.json)

Ctrl+Shift+F1  → prev_theme
Ctrl+Shift+F2  → cycle_subtopic
Ctrl+Shift+F3  → next_theme
Ctrl+Shift+F4  → review_last
Ctrl+Shift+F5  → toggle_panel
Ctrl+Shift+F6  → reserved
Ctrl+Shift+F7  → capture_green
Ctrl+Shift+F8  → capture_orange
Ctrl+Shift+F9  → capture_red

Build Phases

Phase 1 — Core Capture Loop ✓ target

  • [x] Extension scaffold: manifest.json, vite.config.js, package.json
  • [x] content.js: keyboard listener, text/URL grab, source_location object, PDF detection
  • [x] background.js: WebSocket client, message relay, offline buffer via chrome.storage.local
  • [x] Python server: websocket_server.py, state_manager.py, file_io.py, config.py, main.py
  • [x] Panel: React bootstrap, FeedView, FeedCard (no connections yet)

Phase 2 — Navigation

  • [ ] Theme/subtopic cycling (F1/F2/F3)
  • [ ] HUD toast in browser (current theme/subtopic, weight color)
  • [ ] HUD PDF page input (when page number unknown)
  • [ ] Review last + undo (F4)
  • [ ] Panel toggle (F5)
  • [ ] keybind_listener.py (pynput global hotkeys)
  • [ ] PROMPT_NEW_SUBTOPIC flow

Phase 3 — Connections + Graph

  • [ ] Drag-to-connect in FeedView (SVG overlay approach)
  • [ ] ConnectionPopover (relationship type selector)
  • [ ] GraphView with React Flow
  • [ ] Node grouping by subtopic
  • [ ] Toggle Feed ↔ Graph in header

Phase 4 — Polish + Export

  • [ ] Filter bar (theme, subtopic, weight)
  • [ ] Text search in feed
  • [ ] Export JSON (EXPORT event → chrome.downloads.download blob)
  • [ ] Session management (new session, load previous)
  • [ ] Add Theme / Add Subtopic UI flows

Key Technical Decisions

Decision Choice Reason
Primary target Edge (Chromium) Chrome APIs work as-is; user preference
Firefox Not supported Needs sidebar_action + browser.* API rewrite
Side panel chrome.sidePanel Native, stable, no popup instability
UI framework React via Vite Component model suits feed + graph
Graph library React Flow Purpose-built, free, well-maintained
Local server Python + websockets Simple, cross-platform
Global keybinds pynput Works across all platforms
State useReducer + Context No Redux overhead needed
Storage Flat JSON per session Inspectable, git-friendly, LLM-feedable
Export WS EXPORT event + chrome.downloads No separate HTTP server needed
Drag-to-connect SVG overlay on mousedown Avoids ondragover issues in scrollable lists
PDF page URL hash + HUD manual input Content scripts can't access PDF viewer internals

config.json

{
  "websocket_port": 8765,
  "session_dir": "./server/data/sessions",
  "keybinds": {
    "prev_theme":     "ctrl+shift+f1",
    "cycle_subtopic": "ctrl+shift+f2",
    "next_theme":     "ctrl+shift+f3",
    "review_last":    "ctrl+shift+f4",
    "toggle_panel":   "ctrl+shift+f5",
    "reserved":       "ctrl+shift+f6",
    "capture_green":  "ctrl+shift+f7",
    "capture_orange": "ctrl+shift+f8",
    "capture_red":    "ctrl+shift+f9"
  },
  "default_themes": ["Research", "Uncategorized"],
  "default_subtopics": ["General"],
  "toast_duration_ms": 2000
}

Python Server Dependencies (requirements.txt)

websockets>=12.0
pynput>=1.7
python-dateutil