Files
vdm/extension
samiandClaude Sonnet 5 ec288db5ea ext: context-menus.ts — link/media menu items + grab-tab command
docs/05 §3: "Download with Velox" on a link (linkUrl) or a media element
(srcUrl) hands that one URL to download.add with the page as referrer; a
configurable command (Ctrl+Shift+U) does the same for the active tab. These
are explicit user requests, so they skip shouldCapture and the fail-open
path — a failure is surfaced to the user via notifications instead.

The page/selection "Download all links…" item waits on the content-script
link harvester (build-order step 7) and will be added there.

manifest: commands.velox-grab-current-tab. 9 tests. 110 total, lint clean.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_012Y9RU58hD1BuwP82DySUHk
2026-09-10 15:41:54 +04:00
..

Owner: lane EXT. See ../docs/agents/AGENT-EXT.md and ../docs/05-extension-spec.md. Firefox MV3, TypeScript. Zero download logic.

Layout

src/background/transport/   Transport interface + WebSocket and native-messaging impls,
                            runtime picker. See docs/adr/0003 for why there are two.
src/shared/protocol/        GENERATED from ../contracts — never hand-edit.
tests/                      vitest; webextension-polyfill is mocked in tests/setup.ts.

Develop

npm ci
npm run typecheck        # tsc --noEmit, strict
npm test                 # vitest run
npm run lint             # web-ext lint (AMO rules) — needs manifest.json

Build against tools/mockd over the WebSocket transport; veloxd is not required.

Transport quick start

import { createTransport } from './src/background/transport/index.js';

const t = await createTransport();            // reads the Options override; default 'auto'
t.onStateChange((s) => renderDot(s));
const rules = await t.call('capture.getRules', {});

createTransport resolves with a live, self-reconnecting transport. When veloxd is not up yet it still resolves (WebSocket, status disconnected, retrying) — callers render the red dot. It rejects only when the user explicitly forced native messaging and that failed. Capture code treats every call() rejection as fail-open.