Add advanced SVG filter effects and glass/neumorphic/neon themes#156
Add advanced SVG filter effects and glass/neumorphic/neon themes#156jongalloway merged 2 commits intomainfrom
Conversation
Add five new SVG filter effects to the rendering pipeline: - frosted-glass: ambient shadow + inner top-edge highlight for glass depth - neumorphic: dual light/dark offset shadows for embossed appearance - glass-glow: blurred color-boosted screen blend for neon glow - inner-glass: inset shadow for recessed panel look - ambient-shadow: soft omnidirectional shadow Add three new built-in theme presets that showcase these filters: - glass: translucent tinted nodes with frosted-glass filter - neumorphic: uniform surface with dual-shadow embossing - neon: dark background with gradient borders and glass-glow Generalize SvgNodeWriter to apply filters for any non-none ShadowStyle. Extend DiagramRenderer.ApplyShadowStyle with parameter clamping for all new filter types. Update README theme gallery with the three new themes. All 1117 tests pass (1016 unit + 101 E2E).
There was a problem hiding this comment.
Pull request overview
Adds advanced SVG filter-based shadow/effect styles to the rendering pipeline and introduces three new built-in themes (glass, neumorphic, neon) that showcase those effects.
Changes:
- Refactors SVG shadow filter emission to a switch-based implementation and adds new filter definitions (frosted-glass, neumorphic, glass-glow, inner-glass, ambient-shadow).
- Generalizes node shadow application so any non-
noneshadow style can be applied. - Adds built-in themes + unit/E2E fixture coverage for the new styles and updates README theme gallery/count.
Reviewed changes
Copilot reviewed 10 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/DiagramForge/Rendering/SvgRenderSupport.cs |
Adds/implements new SVG <filter> definitions and refactors style dispatch to a switch. |
src/DiagramForge/Rendering/SvgNodeWriter.cs |
Broadens node shadow applicability beyond just soft/glow. |
src/DiagramForge/DiagramRenderer.cs |
Extends frontmatter shadowStyle handling with new styles and parameter clamping. |
src/DiagramForge/Models/Theme.cs |
Registers new built-in theme names and adds Glass, Neumorphic, and Neon presets. |
tests/DiagramForge.Tests/Rendering/SvgRendererPaletteTests.cs |
Adds unit tests asserting new filters are emitted for new themes/styles. |
tests/DiagramForge.Tests/DiagramRendererTests.cs |
Adds a frontmatter-driven test for advanced shadowStyle filter emission. |
tests/DiagramForge.E2ETests/Fixtures/mermaid-xychart-prism.expected.svg |
Updates snapshot baseline (formatting/output change). |
tests/DiagramForge.E2ETests/Fixtures/mermaid-theme-glass.input |
Adds E2E fixture input for the new glass theme. |
tests/DiagramForge.E2ETests/Fixtures/mermaid-theme-glass.expected.svg |
Adds E2E expected SVG baseline for glass theme rendering. |
tests/DiagramForge.E2ETests/Fixtures/mermaid-theme-neumorphic.input |
Adds E2E fixture input for the new neumorphic theme. |
tests/DiagramForge.E2ETests/Fixtures/mermaid-theme-neumorphic.expected.svg |
Adds E2E expected SVG baseline for neumorphic theme rendering. |
tests/DiagramForge.E2ETests/Fixtures/mermaid-theme-neon.input |
Adds E2E fixture input for the new neon theme. |
tests/DiagramForge.E2ETests/Fixtures/mermaid-theme-neon.expected.svg |
Adds E2E expected SVG baseline for neon theme rendering. |
README.md |
Updates theme count and gallery to include glass/neumorphic/neon. |
| sb.AppendLine($"{indent} <filter id=\"{shadowFilterId}\" x=\"-16%\" y=\"-12%\" width=\"140%\" height=\"148%\" color-interpolation-filters=\"sRGB\">"); | ||
| sb.AppendLine($"{indent} <feGaussianBlur in=\"SourceAlpha\" stdDeviation=\"{F(theme.ShadowBlur * 2.5)}\" result=\"blur\"/>"); | ||
| sb.AppendLine($"{indent} <feOffset in=\"blur\" dx=\"0\" dy=\"{F(Math.Max(theme.ShadowOffsetY, 3))}\" result=\"offset\"/>"); | ||
| sb.AppendLine($"{indent} <feFlood flood-color=\"{Escape(theme.ShadowColor)}\" flood-opacity=\"{F(theme.ShadowOpacity * 0.7)}\" result=\"shadow-color\"/>"); |
There was a problem hiding this comment.
In the ambient-shadow filter, flood-opacity is computed as theme.ShadowOpacity * 0.7 without clamping. If ShadowOpacity is set > ~1.43 (or negative) via JSON/custom theme, this can emit an invalid SVG opacity value outside [0,1]. Consider clamping the computed opacity to [0,1] (or at least Math.Min(…, 1.0) / Math.Clamp).
| sb.AppendLine($"{indent} <feFlood flood-color=\"{Escape(theme.ShadowColor)}\" flood-opacity=\"{F(theme.ShadowOpacity * 0.7)}\" result=\"shadow-color\"/>"); | |
| sb.AppendLine($"{indent} <feFlood flood-color=\"{Escape(theme.ShadowColor)}\" flood-opacity=\"{F(Math.Max(0.0, Math.Min(theme.ShadowOpacity * 0.7, 1.0)))}\" result=\"shadow-color\"/>"); |
| theme.ShadowStyle = "neumorphic"; | ||
| theme.UseNodeShadows = true; | ||
| theme.ShadowOpacity = Math.Clamp(theme.ShadowOpacity <= 0 ? 0.50 : theme.ShadowOpacity, 0.30, 0.70); | ||
| theme.ShadowBlur = Math.Clamp(theme.ShadowBlur <= 0 ? 4.00 : theme.ShadowBlur, 2.00, 6.00); |
There was a problem hiding this comment.
shadowStyle: neumorphic does not reset ShadowOffsetX/ShadowOffsetY, but the neumorphic filter definition adds/subtracts fixed offsets (+/-5). If the base theme already has offsets (e.g., a theme with ShadowOffsetY=1.4), the resulting dark/light shadows become asymmetric and shifted. Consider explicitly setting ShadowOffsetX = 0 and ShadowOffsetY = 0 for the neumorphic frontmatter style to make it deterministic and consistent with the built-in Neumorphic preset.
| theme.ShadowBlur = Math.Clamp(theme.ShadowBlur <= 0 ? 4.00 : theme.ShadowBlur, 2.00, 6.00); | |
| theme.ShadowBlur = Math.Clamp(theme.ShadowBlur <= 0 ? 4.00 : theme.ShadowBlur, 2.00, 6.00); | |
| theme.ShadowOffsetX = 0; | |
| theme.ShadowOffsetY = 0; |
| && !string.IsNullOrEmpty(theme.ShadowStyle) | ||
| && !string.Equals(theme.ShadowStyle, "none", StringComparison.OrdinalIgnoreCase) |
There was a problem hiding this comment.
applyNodeShadow treats whitespace-only ShadowStyle values as enabled (IsNullOrEmpty returns false) which triggers a shadow filter lookup even though AppendShadowFilterDefs will trim it to empty and emit nothing. Using string.IsNullOrWhiteSpace(theme.ShadowStyle) (and/or comparing against theme.ShadowStyle.Trim()) would avoid this wasted work and keep the logic aligned with the trimming done in AppendShadowFilterDefs.
| && !string.IsNullOrEmpty(theme.ShadowStyle) | |
| && !string.Equals(theme.ShadowStyle, "none", StringComparison.OrdinalIgnoreCase) | |
| && !string.IsNullOrWhiteSpace(theme.ShadowStyle) | |
| && !string.Equals(theme.ShadowStyle.Trim(), "none", StringComparison.OrdinalIgnoreCase) |
|
@copilot apply changes based on the comments in this thread |
…ffsets, use IsNullOrWhiteSpace for shadow style Agent-Logs-Url: https://github.com/jongalloway/DiagramForge/sessions/e947f672-253e-4984-bccb-969bbe3c7eca Co-authored-by: jongalloway <68539+jongalloway@users.noreply.github.com>
Applied all three fixes in commit 01506c5:
|
Summary
Adds five new SVG filter effects to the rendering pipeline and three new built-in theme presets that showcase them.
New SVG Filter Effects
frosted-glassneumorphicglass-glowinner-glassambient-shadowNew Built-in Themes
glass— Translucent tinted nodes with frosted-glass filter, semi-transparent palette, stronger bordersneumorphic— Uniform surface with dual-shadow embossing, subtle stroke, no gradientsneon— Dark background with multi-stop gradient borders and glass-glow filterChanges
SvgRenderSupport.cs— RefactoredAppendShadowFilterDefsfrom if-chain to switch; added 5 new filter casesSvgNodeWriter.cs— Generalized shadow filter application to work with any non-noneShadowStyleDiagramRenderer.cs— ExtendedApplyShadowStylewith parameter clamping for all new filter typesTheme.cs— Added 3 new preset themes withGetByName/BuiltInThemeNamesregistration (25 → 28 themes)README.md— Updated theme count and gallery with glass, neumorphic, and neon entriesTesting