How to work on DOMPin.
dompin/
├── packages/
│ └── extension/ # Chrome extension (Vite + crxjs + React 18)
│ └── src/
│ ├── background/ # service worker: sessions, vault, transcription, file writes, picker gate
│ ├── content/ # picker overlay, comment popup, capture pipeline
│ ├── sidepanel/ # side-panel UI (React): wizard, session card, picker hero, pin list
│ ├── options/ # options page (React)
│ └── common/ # shared types, settings, messaging, vault handle
├── examples/
│ └── demo-app/ # static page for manual picker QA
└── docs/ # installation, architecture, file schema, this file
pnpm install
pnpm buildpnpm build produces packages/extension/dist. That directory is the loadable extension.
To create a clean archive for a non-technical recipient:
pnpm package:share -- /path/to/outputThe archive contains the prebuilt extension and Spanish installation instructions. It excludes source maps, the demo app, source code, package-manager files, and development dependencies.
pnpm --filter @dompin/extension devVite rebuilds dist/ on file changes. After the first build, load dist/ as an unpacked extension at chrome://extensions (Developer mode → Load unpacked). Subsequent edits hot-reload automatically.
A second helpful pane:
pnpm typecheck --watchpnpm typecheck # strict TypeScript across the workspace
pnpm format # Prettier write
pnpm format:check # Prettier verify, used by CI
pnpm validate # typecheck + build, the same combo CI runsThe repository ships with a strict tsconfig.base.json: strict, noUncheckedIndexedAccess, verbatimModuleSyntax. Imports of types must use import type syntax. Index access on arrays returns T | undefined and must be narrowed.
pnpm build.- Load
packages/extension/distas an unpacked extension atchrome://extensions. - Click the DOMPin icon to open the side panel. The first time, the wizard walks you through picking a vault folder. A scratch folder under
/tmpis fine for experiments. - Open
examples/demo-app/index.html(served from the extension) or any real site you have permission to annotate. - In the side panel, click Start new session in the Session card. Name it, press Enter, and click Start picking.
- Hover over a card or button, click to anchor, type a comment, and press Enter. The picker stays on for the next pin.
- Try the one-shot shortcut: press
Cmd+Shift+.(orCtrl+Shift+.) on a fresh element. The picker captures one element and auto-stops. - Try the right-click flow on a hover-only element: right-click → Annotate element with DOMPin → confirm the popup captures that element without dismissing it.
- Try region capture: with the picker active, click and drag a rectangle over part of the page. The dashed region should stay visible until the popup opens, and
NN.jsonshould includeregion.elements. - Configure OpenAI or ElevenLabs in the options page, record a short audio note from the popup, stop recording, and confirm the transcript is inserted into the visible comment before submitting.
- Attach a small file from the popup and submit. The vault should contain
NN.attachments/<file>and the Markdown/JSON should link to it. - Open the vault folder. A new domain subfolder and a session subfolder should contain
01.md,01.element.png,01.viewport.png,01.json, and any01.attachments/directory for that pin. - Click End session in the side panel: the picker stops, the session card returns to the empty state.
The session card also lets you rename the active session, start a new one, or end it. The pin list below shows annotations for the current page with edit and delete in place.
- TypeScript strict, no implicit
any. - Many small files: 200-400 lines typical, 800 max per file.
- Comments only when the why is non-obvious; never paraphrase the code.
- React lives in the side panel and the options page. The content script avoids React except for the comment popup, which mounts inside a Shadow DOM root for isolation.
- All file writes go through the helpers in
src/background/. Never callshowSaveFilePickerdirectly from a UI surface — the wizard'sshowDirectoryPickeris the only direct File System Access API call from a UI. - Picker access is gated by an active session. The shared check lives in
src/background/picker-gate.ts; route any new entry point through it. - Audio transcription runs through
src/background/transcription.ts. Content scripts should send recorded audio to the background instead of calling provider APIs directly.
v0.x until the file schema stabilizes. Each release:
- Bump the version in
package.json,packages/extension/package.json, andpackages/extension/manifest.json. - Update
CHANGELOG.md. - Run
pnpm validateandpnpm package:share -- release. git tag vX.Y.Z && git push --tags.gh release create vX.Y.Zwith notes drawn from the changelog and the packaged ZIP.- Once the schema is stable, publish a packaged build to the Chrome Web Store.