Skip to content

feat(api): inline highlight chips — text on a rounded background#225

Merged
DemchaAV merged 3 commits into
developfrom
feat/inline-highlight-chip
Jun 22, 2026
Merged

feat(api): inline highlight chips — text on a rounded background#225
DemchaAV merged 3 commits into
developfrom
feat/inline-highlight-chip

Conversation

@DemchaAV

Copy link
Copy Markdown
Owner

Why

GraphCompose can recolour and restyle inline text, but it could not paint a
background behind an inline run — inline text carries only
fontName/size/decoration/color, and backgrounds existed only at block level
(ShapeContainerNode). So the GitHub inline code look (mono text on a light
rounded chip) and inline status badges were impossible. The graphcompose-markdown
consumer is blocked on this for its inline-code rendering.

What changed

  • A new sealed InlineRun variant InlineHighlightRun carrying an
    InlineBackground(fill, cornerRadius, padding) (a backend-neutral
    document.style value). DSL: RichText.highlight(text, style, bg, radius, padding) is the primitive; code(text) ships engine defaults (monospace + a
    light chip) and chip(text, fg, bg) colours a badge — with the matching
    ParagraphBuilder.inlineHighlight / inlineCode / inlineChip and a highlight
    overload taking DocumentLinkOptions, so a chip can also be a link.
  • The chip is text, not an atomic icon — it rides the existing text-span path.
    The run lowers (in TextFlowSupport.tokenizeInlineRuns) to one InlineTextToken
    tagged with the background + a group marker + lead/trail padding;
    ParagraphTextSpan gains a nullable background; the PDF handler's renderChip
    paints a rounded rect (reusing PdfShapeGeometry.roundedRectPath) behind the
    glyphs, in an isolated text block offset by the left padding. Horizontal padding
    folds into wrapWidth() and counts toward wrapping; vertical padding overflows
    the line box without changing line metrics, so splitParagraph / pagination are
    untouched. ParagraphNode.inlineTextRuns() degrades a chip to plain text, so
    text-only backends (DOCX) keep the text and drop the fill.
  • A chip is one atomic token (covers inline code and single-line badges); the
    whitespace trimmers and the long-token break-path leave chip tokens intact, so a
    leading-space chip first on a line keeps its fill.

Scope: the primitive + single-fragment chip. Multi-word coalescing and
wrapping a chip across line breaks land in a follow-up
— the token already
carries the group marker, so that change is purely additive.

Verification

./mvnw test -pl .BUILD SUCCESS, full suite green. New coverage:

  • InlineHighlightRunTest — model/DSL: construction, null-fill + non-finite/negative-radius rejection, code/chip/highlight produce the expected run (mono font, padding, link).
  • InlineHighlightRenderTest — chip width = glyphs + horizontal padding; fill paints behind the glyphs (pixel assert) with text preserved; radius clamp; linked chip emits a PDAnnotationLink; code() renders on a chip; a leading-space chip first-on-line keeps its fill; a mid-line chip keeps the text on both sides; a multi-word chip is one span and an embedded newline collapses; an over-wide chip renders without throwing.

./mvnw -f examples/pom.xml compile green; InlineHighlightExample is wired into GenerateAllExamples, renders, and was visually verified.

Lane: shared-engine (layout + render) + canonical DSL. New public API, all @since 1.9.0; CHANGELOG updated.

Independent of any open branch.

DemchaAV and others added 3 commits June 22, 2026 17:37
A new sealed InlineRun variant, InlineHighlightRun, draws inline text on a
rounded, padded background fill — the GitHub inline `code` look and inline
status badges. RichText.highlight(text, style, bg, radius, padding) is the
primitive; code(text) ships engine defaults (monospace + a light chip),
chip(text, fg, bg) colours a badge, plus the matching ParagraphBuilder
.inlineHighlight / inlineCode / inlineChip and a highlight overload taking
DocumentLinkOptions so a chip can also be a link.

It rides the existing text-span path: the run lowers to a text token carrying
a new InlineBackground(fill, cornerRadius, padding), ParagraphTextSpan gains a
nullable background, and the PDF handler paints a rounded rect (reusing
PdfShapeGeometry.roundedRectPath) behind the glyphs. Horizontal padding widens
the run's advance and counts toward wrapping; vertical padding overflows the
line box without changing line metrics. Text-only backends keep the text and
drop the fill.

First cut: a chip is one atomic token (covers inline code and single-line
badges); multi-word coalescing and wrapping a chip across line breaks land in
a follow-up.

Adds InlineHighlightRunTest + InlineHighlightRenderTest and InlineHighlightExample.
A highlight chip first-on-line whose text begins with whitespace was rebuilt
by the leading-whitespace trimmer via the plain token factory, dropping the
background, padding and group marker — so the chip rendered as plain text with
no fill (the chip(" Paid ", ...) badge idiom hit it). Exempt chip tokens
(highlightGroup != null) from trimLeadingIfInlineLineStart,
trimTrailingWhitespaceTokens and the visible-content check, mirroring the
long-token guard.

Also hardens the new surface: the DSL highlight/chip adders reject a null
background at the boundary with a param-named message; renderChip sanitizes
before the fill so an unencodable chip never paints a glyph-less rect (and its
band doc is corrected); the text-only degrade path collapses newlines like the
PDF tokenizer. Adds regression tests for the leading-space chip, a mid-line
chip, the atomic/no-wrap invariant (one span + newline collapse), and an
over-wide chip.
@DemchaAV DemchaAV merged commit 1ab9b48 into develop Jun 22, 2026
11 checks passed
@DemchaAV DemchaAV deleted the feat/inline-highlight-chip branch June 22, 2026 19:02
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.

1 participant