feat(api): inline highlight chips — text on a rounded background#225
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 inlinecodelook (mono text on a lightrounded chip) and inline status badges were impossible. The
graphcompose-markdownconsumer is blocked on this for its inline-code rendering.
What changed
InlineRunvariantInlineHighlightRuncarrying anInlineBackground(fill, cornerRadius, padding)(a backend-neutraldocument.stylevalue). DSL:RichText.highlight(text, style, bg, radius, padding)is the primitive;code(text)ships engine defaults (monospace + alight chip) and
chip(text, fg, bg)colours a badge — with the matchingParagraphBuilder.inlineHighlight / inlineCode / inlineChipand ahighlightoverload taking
DocumentLinkOptions, so a chip can also be a link.The run lowers (in
TextFlowSupport.tokenizeInlineRuns) to oneInlineTextTokentagged with the background + a group marker + lead/trail padding;
ParagraphTextSpangains a nullablebackground; the PDF handler'srenderChippaints a rounded rect (reusing
PdfShapeGeometry.roundedRectPath) behind theglyphs, in an isolated text block offset by the left padding. Horizontal padding
folds into
wrapWidth()and counts toward wrapping; vertical padding overflowsthe line box without changing line metrics, so
splitParagraph/ pagination areuntouched.
ParagraphNode.inlineTextRuns()degrades a chip to plain text, sotext-only backends (DOCX) keep the text and drop the fill.
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/highlightproduce 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 aPDAnnotationLink;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 compilegreen;InlineHighlightExampleis wired intoGenerateAllExamples, 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.