backnotprop / backnotprop/plannotator
Carry elementContext through the core html-anchor host helpers (follow-up to #1517)
- Dominant language
- TypeScript
- Stars
- 8.7k
- Forks
- 649
- Avg merge
- 11h 12m
- Merged PRs (30d)
- 109
Description
#1517 added `elementContext` (and per-target `context`) to raw-HTML / live-app pinpoint annotations in `@plannotator/ui`, captured by the bridge and validated at the parent trust boundary. It deliberately left `@plannotator/core/html-anchor` untouched, so a host that persists through `buildPersistedHtmlAnchor` and reads through `projectHostThreads` drops the field on save and never gets it back on projection. Workspaces is such a host and has asked for this; their read-only review of #1517 is the source of the requirements below.
## Requirements (from the Workspaces host review)
1. `parseHtmlElementContext` lives in `@plannotator/core/html-anchor` next to `parseHtmlElementAnchor`, and `@plannotator/ui` re-uses it rather than keeping a hand-mirrored validator (the rule since core 0.25.0: no mirror validators).
2. `PersistedHtmlAnchor` carries `elementContext?` and each `HtmlAnnotationTarget` carries `context?`. **Key order:** append `elementContext` AFTER `htmlAdditionalTargets` so rows without it serialize byte-identically; the host's codec test pins the wire fingerprint.
3. `buildPersistedHtmlAnchor` sheds contexts BEFORE targets under `maxBytes`. The host's server cap stays 16 KiB, and one primary at 2 KiB plus 16 extras at 1 KiB already exceeds it; raising `DEFAULT_HTML_ANCHOR_MAX_BYTES` is not an engineering default (the 48 KiB figure in the design write-up was not shipped and core stays at 16 KiB).
4. `projectHostThreads` returns `elementContext` / per-target `context` on the projection so the host's panel (and the panel's per-row Copy, which calls `exportAnnotationEntry`) sees it. Paint never reads it; the repaint path already posts only anchors to the bridge.
5. An export entry point that takes the projected/host shape and can print the identity lines WITHOUT the fenced outline (an option on `exportAnnotationEntry` / `elementContextExportBlock`), because in a model turn the 600-char outline is the expensive part per annotation.
## Also noted by the host
- The panel's Copy is now gated on `elementContext` only (#1520), so anchor-only host rows do not change on a package bump.
- The context never exposes more than the document bytes the viewer can already open; href scrubbing matters to hosts where a share link is the credential.
Full write-up on the host side: `/Users/ramos/workspaces/research/integration/PLANNOTATOR-HTML-ELEMENT-CONTEXT-COMPARISON-2026-09-12.md` (local, not in this repo).
Contributor guide
Research direction
Start in @plannotator/core/html-anchor, reading parseHtmlElementContext, PersistedHtmlAnchor, HtmlAnnotationTarget, and buildPersistedHtmlAnchor, then trace projectHostThreads into the panel and exportAnnotationEntry/elementContextExportBlock. Use the host codec test to preserve the stated wire fingerprint and verify that contexts survive persistence and projection, while export can emit identity lines without the fenced outline.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend, tooling
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100