Skip to content

Support inherited SVG paint attributes#990

Merged
t-regbs merged 2 commits into
mainfrom
feature/svg-parser-inheritance
May 3, 2026
Merged

Support inherited SVG paint attributes#990
t-regbs merged 2 commits into
mainfrom
feature/svg-parser-inheritance

Conversation

@t-regbs

@t-regbs t-regbs commented May 2, 2026

Copy link
Copy Markdown
Collaborator
  • Add SVG parser support for inherited root/group paint attributes.
  • Propagate inherited fill, stroke, and stroke styling through SVG groups and shapes.

📝 Changelog

If this PR introduces user-facing changes, please update the relevant Unreleased section in changelogs:

@t-regbs t-regbs requested a review from egorikftp May 2, 2026 19:48
@coderabbitai

coderabbitai Bot commented May 2, 2026

Copy link
Copy Markdown

Walkthrough

The changes add SVG stroke-related optional attributes to the root model and a nullable fill to SVG.Group, introduce an internal PaintContext to carry fill and stroke properties through parsing, and convert node converters to use Kotlin context receivers so children inherit paint properties. Element converters resolve fill from the context when absent. SVG.toImageVector() and group conversion create and use cascaded paint contexts. Tests and test helpers were updated to exercise root/group inheritance scenarios. The Kotlin compiler is enabled with -Xcontext-parameters.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Support inherited SVG paint attributes' directly and clearly summarizes the main change: adding support for attribute inheritance in the SVG parser.
Description check ✅ Passed The description clearly explains the core changes (inherited root/group paint attributes and propagation through SVG groups/shapes) and includes the required changelog template with checkboxes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/svg-parser-inheritance

Tip

💬 Introducing [Slack Agent](https://www.coderabbit.ai/agent): Turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get your free trial and get 200 agent minutes per Slack user (a $50 value).


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
components/parser/kmp/svg/src/commonTest/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/SVGParserTest.kt (1)

68-129: ⚡ Quick win

Add coverage for group stroke and inherited stroke opacity/miter.

The new tests only assert root stroke color/width/cap/join and group fill. The new branches in SVG.Group.toVectorGroup() and SVG.Child.getSVGStrokeWithDefaults() for group-level stroke cascade plus inherited stroke-opacity / stroke-miterlimit can still regress unnoticed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/parser/kmp/svg/src/commonTest/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/SVGParserTest.kt`
around lines 68 - 129, Add tests that exercise group-level stroke inheritance
and stroke-specific attributes: create an SVG with a <g> that sets stroke
(color, width, lineCap, lineJoin) plus stroke-opacity and stroke-miterlimit,
containing child <path> elements without their own stroke; then assert that
SVG.Group.toVectorGroup() and the stroke produced by
SVG.Child.getSVGStrokeWithDefaults() propagate the group's stroke color,
strokeWidth, strokeLineCap, strokeLineJoin, strokeAlpha (from stroke-opacity),
and strokeLineMiter (from stroke-miterlimit) into the resulting IrStroke (e.g.,
IrStroke.Color with IrColor, strokeAlpha float, strokeLineWidth float,
strokeLineCap enum, strokeLineJoin enum, strokeLineMiter float) so regressions
in the new group-level branching are caught.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@components/parser/kmp/svg/src/commonMain/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/SVGParser.kt`:
- Around line 68-76: The path default-fill logic in SVG.Path.toVectorPath uses
the inherited resolvedStrokeColor when deciding to synthesize a black fill,
causing paths to lose their default fill if a parent provides stroke; change the
condition to consider only the path's own strokeColor (not paintContext/stroke
inheritance). Specifically, after computing resolvedFill and
resolvedStrokeColor, set fillColor = if (resolvedFill == null &&
this.strokeColor == null) Black else fillColor so the default black fill is
synthesized only when the path itself has no fill and no explicit stroke
attribute.

---

Nitpick comments:
In
`@components/parser/kmp/svg/src/commonTest/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/SVGParserTest.kt`:
- Around line 68-129: Add tests that exercise group-level stroke inheritance and
stroke-specific attributes: create an SVG with a <g> that sets stroke (color,
width, lineCap, lineJoin) plus stroke-opacity and stroke-miterlimit, containing
child <path> elements without their own stroke; then assert that
SVG.Group.toVectorGroup() and the stroke produced by
SVG.Child.getSVGStrokeWithDefaults() propagate the group's stroke color,
strokeWidth, strokeLineCap, strokeLineJoin, strokeAlpha (from stroke-opacity),
and strokeLineMiter (from stroke-miterlimit) into the resulting IrStroke (e.g.,
IrStroke.Color with IrColor, strokeAlpha float, strokeLineWidth float,
strokeLineCap enum, strokeLineJoin enum, strokeLineMiter float) so regressions
in the new group-level branching are caught.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: bf37b7eb-a700-487e-b625-2da46ff2d4a2

📥 Commits

Reviewing files that changed from the base of the PR and between 7c83d70 and e180a8d.

📒 Files selected for processing (6)
  • components/parser/kmp/svg/build.gradle.kts
  • components/parser/kmp/svg/src/commonMain/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/SVG.kt
  • components/parser/kmp/svg/src/commonMain/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/SVGParser.kt
  • components/parser/kmp/svg/src/commonMain/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/SvgExtensions.kt
  • components/parser/kmp/svg/src/commonTest/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/SVGParserTest.kt
  • components/parser/kmp/svg/src/commonTest/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/Utils.kt

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
components/parser/kmp/svg/src/commonTest/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/SVGParserTest.kt (1)

130-157: ⚡ Quick win

Consider adding a group-level stroke inheritance test.

The PR claims to propagate inherited fill, stroke, and stroke styling through SVG groups and shapes. The new tests cover:

  • ✅ Root stroke → path
  • ✅ Root fill (null) → path defaults to black
  • ✅ Group fill → path

But there is no test for group stroke → path (e.g., <g stroke="#ff0000" stroke-width="1.5">). Adding it would close the coverage gap and give confidence that the PaintContext cascade works symmetrically for stroke at the group level.

✅ Suggested test skeleton
`@Test`
fun parse_path_with_inherited_group_stroke() {
    val svg = svg(fill = "none") {
        """
        <g stroke="#ff0000" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
            <path d="M4 4h16"/>
        </g>
        """
    }

    val actual: List<IrVectorNode> = SVGParser.parse(svg).groups.single().nodes

    assertEquals(
        actual = actual,
        expected = listOf(
            IrVectorNode.IrPath(
                pathFillType = IrPathFillType.NonZero,
                fill = null,
                paths = listOf(
                    IrPathNode.MoveTo(4f, 4f),
                    IrPathNode.RelativeHorizontalTo(16f),
                ),
                fillAlpha = 1f,
                stroke = IrStroke.Color(IrColor(0xFFFF0000)),
                strokeAlpha = 1f,
                strokeLineWidth = 1.5f,
                strokeLineCap = IrStrokeLineCap.Round,
                strokeLineJoin = IrStrokeLineJoin.Round,
                strokeLineMiter = 4f,
            ),
        ),
    )
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/parser/kmp/svg/src/commonTest/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/SVGParserTest.kt`
around lines 130 - 157, Add a parallel unit test that verifies group-level
stroke inheritance: create a new test function (e.g.,
parse_path_with_inherited_group_stroke) similar to
parse_path_with_inherited_group_fill that builds an SVG with <g stroke="#ff0000"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4
4h16"/></g>, call SVGParser.parse(svg) and assert the single IrVectorNode.IrPath
produced has stroke = IrStroke.Color(IrColor(0xFFFF0000)), strokeAlpha = 1f,
strokeLineWidth = 1.5f, strokeLineCap = IrStrokeLineCap.Round, strokeLineJoin =
IrStrokeLineJoin.Round and strokeLineMiter left at default (4f), while fill
remains null; reference SVGParser.parse, IrVectorNode.IrPath and the
stroke-related properties to locate where to assert.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@components/parser/kmp/svg/src/commonTest/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/SVGParserTest.kt`:
- Around line 130-157: Add a parallel unit test that verifies group-level stroke
inheritance: create a new test function (e.g.,
parse_path_with_inherited_group_stroke) similar to
parse_path_with_inherited_group_fill that builds an SVG with <g stroke="#ff0000"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4
4h16"/></g>, call SVGParser.parse(svg) and assert the single IrVectorNode.IrPath
produced has stroke = IrStroke.Color(IrColor(0xFFFF0000)), strokeAlpha = 1f,
strokeLineWidth = 1.5f, strokeLineCap = IrStrokeLineCap.Round, strokeLineJoin =
IrStrokeLineJoin.Round and strokeLineMiter left at default (4f), while fill
remains null; reference SVGParser.parse, IrVectorNode.IrPath and the
stroke-related properties to locate where to assert.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ee12bd1b-c56f-4e37-a153-5087f8399997

📥 Commits

Reviewing files that changed from the base of the PR and between e180a8d and e9bf545.

📒 Files selected for processing (3)
  • components/parser/kmp/svg/src/commonMain/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/SVGParser.kt
  • components/parser/kmp/svg/src/commonTest/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/SVGParserTest.kt
  • components/parser/kmp/svg/src/commonTest/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/Utils.kt
✅ Files skipped from review due to trivial changes (1)
  • components/parser/kmp/svg/src/commonMain/kotlin/io/github/composegears/valkyrie/parser/kmp/svg/SVGParser.kt

@t-regbs t-regbs merged commit a12f032 into main May 3, 2026
3 checks passed
@t-regbs t-regbs deleted the feature/svg-parser-inheritance branch May 3, 2026 08:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants