Skip to content

feat(edges): render edge labels as HTML with labelHtml - #82

Merged
zachiler merged 1 commit into
getartisanflow:v0.2.3-alphafrom
webard:feat/edge-label-html
Aug 13, 2026
Merged

feat(edges): render edge labels as HTML with labelHtml#82
zachiler merged 1 commit into
getartisanflow:v0.2.3-alphafrom
webard:feat/edge-label-html

Conversation

@webard

@webard webard commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

What

An edge can now ask for its labels to be rendered as HTML:

{ id: 'e1', source: 'a', target: 'b', label: 'over the limit<br><em>and no manager on shift</em>', labelHtml: true }

Why

ensureLabel writes label text with textContent, so anything but plain text shows as the tags themselves. A label that wants a line break, a small icon, or a piece of emphasis has no way to ask for one — white-space: pre-wrap on .flow-edge-label only reaches newlines that were actually typed, and the label div is already an HTML element, so the capability is there, just not reachable.

Where this came from: a workflow editor where the line between two nodes carries the reason the branch exists ("over the credit limit / and no manager on shift"). Two short lines read far better than one long one, and the editor writes those labels itself.

Shape

  • labelHtml?: boolean on FlowEdge, default off — nothing renders differently until an edge asks.
  • It covers all three positions (label, labelStart, labelEnd): one decision about one edge, not three flags.
  • The value goes to innerHTML unchanged and is trusted the way any framework trusts HTML it is handed. The docstring on the type and the labels page both say to sanitise user input first.
  • Both branches skip the write when the DOM already holds the string, so the label block still costs nothing on effect re-runs that don't change it.

Tests

Three cases in src/plugin/directives/flow-edge.test.ts (the existing mountEdges harness, with the container marked as the viewport so labels have somewhere to land):

  • the default still writes text, so markup shows as tags;
  • labelHtml: true renders real elements;
  • the flag reaches labelStart / labelEnd too.

The two HTML cases fail against dev without the source change.

npm run test — 187 files, 3035 tests, all passing.

dist/ is not rebuilt here — source and docs only, so the diff stays reviewable. I had pushed a rebuild commit and have since dropped it (force-push); the build is yours to cut at release time.

Not included

No version bump, no CHANGELOG.md entry, no dist/ rebuild, no new dependencies — the changelog has no Unreleased section and releases are cut by the maintainer, so I left that to the release commit. Happy to add an entry if you'd rather have it in the PR.

Edge labels are written with `textContent`, so anything but plain text shows
as the tags themselves — a label that wants a line break, an icon or a piece
of emphasis has no way to ask for one, and `white-space: pre-wrap` on
`.flow-edge-label` only reaches newlines that were typed.

`labelHtml: true` on an edge switches its labels to `innerHTML`. It covers all
three positions (`label`, `labelStart`, `labelEnd`), because they are one
decision about one edge, and defaults to off, so nothing renders differently
until an edge asks. The value is trusted the way any framework trusts HTML it
is handed; the type's docstring and the labels page both say to sanitise user
input first.

Both branches skip a write when the DOM already holds the string, so the label
block still costs nothing on the effect re-runs that don't change it.
@zachiler

Copy link
Copy Markdown
Member

Reviewed — this is clean. The one thing I'd normally flag on an innerHTML path is the XSS surface, and you've already handled it the right way: off by default, and the trust boundary is called out in both the type docstring and the labels doc, which matches how x-html/v-html are treated elsewhere. One flag covering all three label positions is the right call, and the !== text guard on both write paths is a nice touch — labels stop re-writing on effect runs that don't change them.

I'll fold this into a v0.2.3-alpha batch with your other pending PRs and cut the dist/ rebuild at release, so leaving it out here was exactly right. Thanks!

@zachiler
zachiler changed the base branch from dev to v0.2.3-alpha August 13, 2026 02:44
@zachiler
zachiler merged commit bd1e2a1 into getartisanflow:v0.2.3-alpha Aug 13, 2026
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.

2 participants