Skip to content

feat(core): carry elementContext through html-anchor host helpers - #1549

Merged
backnotprop merged 5 commits into
backnotprop:mainfrom
FNDEVVE:fix/1521-element-context-core
Sep 17, 2026
Merged

backnotprop merged 5 commits into
backnotprop:mainfrom
FNDEVVE:fix/1521-element-context-core

Conversation

@FNDEVVE

@FNDEVVE FNDEVVE commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Implements the five follow-up requirements from the Workspaces host review so hosts persisting through the core helpers no longer lose element context:

  1. parseHtmlElementContext moves into @plannotator/core/html-anchor next to parseHtmlElementAnchor; useHtmlAnnotation imports and re-exports it and the hand-mirrored validator is deleted.
  2. PersistedHtmlAnchor gains elementContext? (serialized after htmlAdditionalTargets, so rows without it are byte-identical) and HtmlAnnotationTarget gains context?.
  3. buildPersistedHtmlAnchor sheds per-target contexts, then the primary context, before dropping targets under maxBytes; the 16 KiB cap is unchanged.
  4. projectHostThreads projects both context fields through to host panels and per-row copy.
  5. New includeOutline export option (default true, output unchanged) prints the identity lines without the fenced outline for model turns.

Testing: bun test packages/core/html-anchor.test.ts packages/ui/utils/parser.test.ts (214 pass, incl. wire-fingerprint, shed-order, projection, and outline-less export tests), full bun run typecheck clean.

Closes #1521.

FNDEVVE and others added 5 commits September 16, 2026 13:16
Persist and project elementContext through the core host helpers; move the validator into core and drop the ui mirror; add outline-less export option.

Closes backnotprop#1521.
`packages/core/html-anchor.ts` gained its own `MAX_PAGE_URL_LENGTH` when
`parseHtmlElementContext` moved there, leaving a second copy in
`useHtmlAnnotation.ts` — exactly the hand-mirrored-constant shape the move
was meant to end. Delete the ui copy, import the core one, and re-export it
from `components/html-viewer` so the host import site and the live-protocol
tests keep reading it from where they already do.
HANDOFF.md still told hosts the element-context gap was open — that
`@plannotator/core/html-anchor` does not carry `elementContext` and that they
must persist and project it themselves. It ships in the ui tarball, so that
line was about to be wrong in a consumer's node_modules.

- HANDOFF.md: the stale gap sentence now names 0.39.0 as the release it
  describes and points forward. A new "Element context through the host seam"
  section says where the validator lives (`parseHtmlElementContext` in
  `@plannotator/core/html-anchor`, re-exported unchanged from
  `components/html-viewer`), what a host passes (`elementContext` on the
  build source, `context` per additional target, `elementContext` on a
  `HostThread`), what it gets back (`PersistedHtmlAnchor.elementContext`,
  `HtmlAnnotationTarget.context`, both on the projection) with the fixed key
  order that keeps context-less rows byte-identical on the wire, and that the
  caps, the attribute allowlist, the shed order and the 16 KiB budget are all
  unchanged — contexts are simply shed before targets. Plus a publishing
  bullet: core moves, so publish core first.
- README.md: the host-seam bullet now mentions the round trip.
- html-anchor.ts: the module doc claimed the validators mirror what ui
  enforces. They no longer mirror it; they are it.
@backnotprop
backnotprop merged commit a42aefc into backnotprop:main Sep 17, 2026
24 checks passed
@backnotprop

Copy link
Copy Markdown
Owner

Thanks @FNDEVVE, this closes #1521 cleanly. The validator move is byte-faithful (no cap, allowlist, or fail-closed path changed), and it is what Workspaces needs to stop carrying a copy.

I pushed three small commits onto your branch rather than round-tripping: a single definition of MAX_PAGE_URL_LENGTH (the ui copy now imports core's), and the HANDOFF / README / module-doc updates so hosts read the new contract instead of the old "gap is open" note. Nothing in your code changed.

Publish order for this one is core 0.25.3 first, then ui 0.40.0 with the pin bumped, since ui now imports a symbol that published core 0.25.2 lacks. That lands in the 0.40.0 bundle PR going up shortly. Merging now.

backnotprop added a commit that referenced this pull request Sep 17, 2026
…heme-aware diagrams; core 0.25.3 (#1557)

* chore(ui): mermaid 12.0.0 (exact), ELK layout by default

Bump @plannotator/ui's mermaid dependency from ^11.17.2 to an exact 12.0.0
and regenerate bun.lock. Mermaid 12 lays flowchart, state, class, ER and
requirement diagrams out with ELK by default (elkjs is now bundled in
mermaid itself), targets Safari 17.4+ / ES2024, and drops the legacy
flowchart/class/state diagram ids. We take 12's defaults rather than
pinning the 11 ones. No API or type changes were needed: MERMAID_CONFIG
(securityLevel 'strict') and the theme mapping apply unchanged, every
generated SVG id keeps its 11.x shape, and the katex import Mermaid makes
for $$ labels is the same call (the math-slot bridge is untouched).

The visual-explainer skill's render gate now expects Mermaid 12.

* feat(editor): load the Mermaid runtime lazily on the first diagram

Drop the eager registration import (@plannotator/ui/utils/mermaid-eager)
from the plan editor entry. Since Mermaid 12 the runtime plus ELK is about
1.8 MB larger, so a plan with no diagram must not pay for it: the block now
resolves the runtime through utils/mermaid's own import('mermaid') on the
first MermaidBlock render. In the chunked share-portal build that moves
mermaid.core (~640 KB) out of the entry chunk; the single-file builds still
inline it through inlineDynamicImports, so nothing changes there except
Mermaid 12's own size.

MermaidBlock shows the source fence under a role=status "Rendering
diagram" line until the first render lands, never the error panel, and
applyMermaidTheme is keyed on the runtime object so the lazily loaded
runtime is themed on its first render like an eagerly registered one.
mermaid-eager stays exported for hosts that want startup registration.

tests/entry-assets.test.ts now asserts the eager Mermaid marker is absent
from both app bundles; DiagramBlock.lazyRetry.test.tsx pins the pending
state.

* docs(ui): HANDOFF 0.40.0 section for Mermaid 12 and the lazy runtime

Add the "Mermaid 12 (0.40.0)" section hosts need to adopt the bump: ELK
layout by default and the other 12 changes we take, the concrete SVG id
patterns (unchanged 11 -> 12, per family), the g.edgePaths declaration-order
change and the select-by-id rule, the Safari 17.4+ / ES2024 floor, the
lazy-load contract (the plan editor no longer imports mermaid-eager; how a
host keeps startup registration; the pending state; measured chunk and
single-file sizes), the theming contract with and without tokens, the 0.40
contrast re-sweep (104 combinations, 0 failures), and the publish order
core 0.25.3 -> ui 0.40.0. Retitle the theme and element-context sections
to 0.40.0, fold the two unreleased publishing bullets into the current
pair, and update the 0.32.0 lazy-renderer text, README and AGENTS for the
new policy.

* chore(core): bump @plannotator/core to 0.25.3

Carries the #1549 html-anchor element-context exports (parseHtmlElementContext,
MAX_ELEMENT_CONTEXT_BYTES, MAX_PAGE_URL_LENGTH) that @plannotator/ui 0.40.0
imports. Publish this before ui 0.40.0.

* chore(ui): bump @plannotator/ui to 0.40.0 on core 0.25.3

Mermaid 12.0.0 (exact), lazy runtime loading in the plan editor, the
theme-aware diagram mapping from #1556, and the #1549 element-context host
seam. Pins @plannotator/core 0.25.3 exactly; bun.lock records both
workspace versions. Publish core 0.25.3 first, then this.

* chore: pin lodash-es 4.18.1 over chevrotain's vulnerable exact pin

Mermaid 12 pulls chevrotain 11.1.2 (via langium), which pins lodash-es
exactly 4.17.23 (CVE-2026-4800 high, CVE-2026-2950 medium), so the
workspace resolved a second, vulnerable copy beside the 4.18.1 that
dagre-d3-es already resolves. A root "overrides" entry forces every
lodash-es range to 4.18.1 (published 2026-04-01, past the 7-day gate);
bun.lock now records a single lodash-es@4.18.1. chevrotain still parses
every diagram grammar under it: all 15 eval diagrams render with zero
errors in the built plan editor, typecheck, the ui utils and MermaidBlock
tests, and the package smoke pass, and the built bundles are byte-for-byte
the same size.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Carry elementContext through the core html-anchor host helpers (follow-up to #1517)

2 participants