Repository navigation
feat: allow inline text styles on dialogue content runs - #381
Merged
Merged
Conversation
Layout elements still reject an authored inline textStyle and must use textStyleId. A run inside the content array of a text or text-revealing element (such as an item of dialogue content) is the writer's text rather than layout design, so it may now carry a small inline textStyle instead of throwing. This lets a bold, italic, underlined, or accented run reach the renderer. A run's textStyle accepts only these fields; any other field throws with the path of the run: - fontWeight: normal, bold, bolder, lighter, or a number from 1 to 1000 - fontStyle: normal, italic, or oblique - textDecoration: none or underline - fill: an opaque hex color When a run has both textStyleId and textStyle, textStyleId resolves first and the inline fields then replace the matching fields. A run with only inline fields hands the renderer just those fields, and Route Graphics merges them over the element's own text style, so the element keeps its font, size, and color. Inline textStyle on the element itself, on hover, click, or rightClick, on furigana, and on elements of any other type still throws as before. Tests: resolveTextStyleIds, addDialogue (adv and nvl), addVisuals, and addOverlayStack render-state cases, an engine-level test that includes L10n packages, and a VT spec with a new reference image for the dialogue render path.
This was referenced Oct 20, 2026
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.
Summary
Layout elements reject any authored inline
textStyleand must usetextStyleId. That rule also rejected dialogue content runs, so a project containingfailed while building the render state with
Inline textStyle is not allowed in layout elements at "root[0].content[0]". Use textStyleId instead. Editors that let a writer emphasise selected dialogue text (bold, italic, underline, an accent color) save exactly this shape, so those projects could not be previewed or exported.A run inside the
contentarray of atextortext-revealingelement is the writer's text rather than layout design, so it may now carry a small inlinetextStyle. Layout elements themselves keep the existing rule.Behavior
A run's inline
textStyleaccepts only these fields:fontWeightnormal,bold,bolder,lighter, or a number from 1 to 1000 (a numeric string also works)fontStylenormal,italic,oblique(the same values text style resources use)textDecorationnone,underlinefill#abcis expanded to six digits (reusesnormalizeHexColor)Any other field (for example
fontSize,fontFamily,shadow) or an invalid value fails fast with the path of the run, in the same style as the existing errors:Inline textStyle.fontSize is not allowed in text content runs at "root[0].content[1]". Only fontWeight, fontStyle, textDecoration, and fill may be authored inline; use textStyleId for other style changesInline textStyle.fill at "root[0].content[1]" must resolve to a hex colorInline textStyle.fontWeight at "..." must be normal, bold, bolder, lighter, or a number from 1 to 1000Inline textStyle.fontStyle at "..." must be normal, italic, or obliqueInline textStyle.textDecoration at "..." must be none or underlineInline textStyle at "..." must be an objectRun with both
textStyleIdandtextStyle:textStyleIdresolves throughresources.textStylesfirst, then the inline fields replace the matching fields of the resolved style. An inlinefillreplaces the text style's fill together with itscolorAlpha.Run with only
textStyle: the renderer receives only the authored fields. Route Graphics merges a run'stextStyleover the element's own style (prepareRichTextSegmentsin bothtextandtext-revealing), so a run with onlyfontWeight: boldkeeps the element's font, size, and color.Unchanged and still throwing the existing errors:
textStyleon a layout element, including with styled runs in its contenthover,click, andrightClicktextStyleon an elementfuriganawith an inlinetextStyle(furigana keeps usingtextStyleId)textStyleon a run of any element type other thantextandtext-revealingOnce templates are rendered the engine cannot distinguish dialogue content from a run written directly in a layout's
contentarray, so both follow the same rule. Runs of legacytext.contentvisuals and history/NVL line content go through the same path.Docs
docs/Concepts.mdanddocs/RouteEngine.mdnow carve out the exception where they state that inlinetextStyleis invalid, anddocs/RouteEngine.mdhas a new "Inline Text Run Styles" section describing the allowlist and thetextStyleId+textStylesemantics.docs/L10n.mdnotes thatline.dialoguereplacement entries contain onlytext.Testing
bun run test: 101 files, 2456 tests pass (baseline onorigin/main: 100 files, 2408 tests).bun run lintandbun run check:test-markerspass.resolveTextStyleIds(allowlisted fields, both-fields overlay, nested elements, furigana, every disallowed key and invalid value, element/hover/click/rightClick/furigana/non-text element still throwing),addDialogue(ADV and NVL line content),addVisuals(text visual content), andaddOverlayStack(history content). The existing throw cases are unchanged.spec/RouteEngine.dialogueRunTextStyle.test.jsruns the real engine: styled ADV runs, ADV append, the authored project data staying untouched, errors, and L10n (a package without a patch for the line keeps the canonical styled runs; a package replacement renders as authored; clearing the package restores the styled runs).vt/specs/dialogue/inline-run-text-style.yamlwith one new reference image (vt/reference/dialogue/inline-run-text-style-01.webp, no existing references changed). It renders bold, italic, accent-fill, andtextStyleId+ inline runs through both atext-revealingand atextelement. Captured with the pinned VT Docker image, byte-identical across three runs, and the report passes. Re-running thedialogue,visual, andlayoutVT folders gives 79 of 81 images matching; the two mismatches (dialogue/character-persist--capture-03,visual/text-shadow-01) are identical on an unmodifiedorigin/main.textDecoration, so underline is not part of the committed VT reference. Underline was checked separately with a Route Graphics 1.48.1 bundle (underline support shipped in 1.48.0): it renders in bothtextandtext-revealing.Notes for review
fillaccepts a raw opaque hex rather than requiring acolorId, because the editor writes hex. Requiring a color resource would be a small change if preferred.colorAlphathroughtextStyleId.line.dialogueL10n replacement entries still accept onlytext, so a translated line does not carry run styles. Lines that no package patches keep their styled runs. AllowingtextStylethere would be a follow-up.src/schemas/presentationActions.yamlis not changed; it does not restrict extra properties on content items.