Skip to content

Add WireframeDslParser for markdown-ui-dsl wireframe diagrams#135

Merged
jongalloway merged 5 commits intomainfrom
copilot/add-wireframe-dsl-parser
Mar 23, 2026
Merged

Add WireframeDslParser for markdown-ui-dsl wireframe diagrams#135
jongalloway merged 5 commits intomainfrom
copilot/add-wireframe-dsl-parser

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 23, 2026

Adds a dedicated WireframeDslParser (SyntaxId = "wireframe") that parses a subset of the markdown-ui-dsl spec into SVG wireframe mockups, keeping it separate from the Conceptual DSL per the PRD category distinction.

Parser

First non-empty line must be wireframe or wireframe: <title>. Supported syntax:

wireframe: Login Screen
::: HEADER :::
  # Login
--- END ---
[ text: Username ]
[ text: Password ]
[ ] Remember me
[x] Accept Terms
( ) Monthly  (x) Yearly
[on] Dark mode
[v] Country {USA, UK, Canada}
|[ Overview ]| Analytics | Settings |
(( New ))
[ IMG: Profile Photo ]
[ Login ](#login)

Containers (||| COLUMN |||, === ROW ===, ::: CARD :::, ::: HEADER :::, ::: FOOTER :::) are closed with --- END --- and nest arbitrarily. Layout-only containment edges (wireframe:containment) are suppressed from SVG output.

Layout (DefaultLayoutEngine.Wireframe.cs)

Three-pass recursive box model:

  1. Bottom-up leaf sizing — each widget sized by content/type
  2. Bottom-up container sizing — containers grow to fit children
  3. Top-down stretch pass — sub-containers (card/header/footer) expand to fill their parent column width; then positions assigned top-down

Rendering (SvgNodeWriter.cs)

Dedicated AppendWireframeNode dispatch with grayscale wireframe palette. Each component type has its own renderer: filled rounded-rect buttons, outlined input fields with placeholder text, checkbox squares with tick marks, radio circles with center dot, pill toggles, dropdowns with chevron+separator, tab bars with active highlight, badge pills, image placeholders with diagonal cross lines, and plain/bold text at heading-appropriate sizes.

Files

File Change
Parsers/Wireframe/WireframeDslParser.cs New — full parser
Layout/DefaultLayoutEngine.Wireframe.cs New — layout engine partial
Rendering/SvgNodeWriter.cs New wireframe dispatch + 15 component renderers
Rendering/SvgRenderer.cs Skip wireframe:containment edges in both render passes
DiagramRenderer.cs Register WireframeDslParser in default constructor
Layout/DefaultLayoutEngine.cs Wire TryLayoutWireframeDiagram dispatch
Tests/Parsers/WireframeDslParserTests.cs 56 unit tests
E2ETests/Fixtures/wireframe-*.input/.expected.svg 3 snapshot fixtures (basic, components, dashboard)

📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

Copilot AI and others added 2 commits March 23, 2026 07:57
Copilot AI changed the title [WIP] Explore adding WireframeDslParser for markdown-ui-dsl Add WireframeDslParser for markdown-ui-dsl wireframe diagrams Mar 23, 2026
Copilot AI requested a review from jongalloway March 23, 2026 08:04
@jongalloway jongalloway marked this pull request as ready for review March 23, 2026 15:18
…, docs

- Add WireframePalette derived from theme's BackgroundColor, TextColor,
  SubtleTextColor, and AccentColor so all 25 built-in themes work
- Fix inline badge parsing: '(( Badge )) trailing text' now emits
  badge + text wrapped in an implicit row for side-by-side rendering
- Remove diagram.Title from wireframe parser (use ::: HEADER ::: instead)
- Restructure E2E fixtures into 3 feature-focused wireframes:
  wireframe-form (default), wireframe-dashboard (dracula),
  wireframe-showcase (catppuccin-latte)
- Update PRD with Wireframe DSL scope (section 5.3)
- Update README: gallery thumbnails, Wireframe DSL section with component
  syntax table, 'If You Want This' entry, link to markdown-ui-dsl
- Update Build-Gallery.ps1 wireframe label handling
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds first-class support for “wireframe” diagrams by introducing a dedicated Wireframe DSL parser, a custom wireframe layout pass, and bespoke SVG rendering for wireframe UI components—integrated into the default DiagramRenderer pipeline and documented via PRD/README + snapshot fixtures.

Changes:

  • Introduces WireframeDslParser (SyntaxId = "wireframe") with unit tests and new E2E fixtures/snapshots.
  • Adds a wireframe-specific layout implementation (DefaultLayoutEngine.Wireframe.cs) and dispatch from DefaultLayoutEngine.
  • Extends rendering to support wireframe nodes and suppress wireframe containment edges from SVG output.

Reviewed changes

Copilot reviewed 13 out of 16 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/DiagramForge/Parsers/Wireframe/WireframeDslParser.cs New parser for markdown-ui-dsl-inspired wireframes; emits containment edges and node metadata.
src/DiagramForge/Layout/DefaultLayoutEngine.cs Adds wireframe layout dispatch in the main layout pipeline.
src/DiagramForge/Layout/DefaultLayoutEngine.Wireframe.cs New multi-pass wireframe box-model layout (leaf sizing, container sizing, stretch, positioning).
src/DiagramForge/Rendering/SvgNodeWriter.cs Adds wireframe rendering dispatch and component renderers.
src/DiagramForge/Rendering/SvgRenderer.cs Suppresses wireframe:containment edges from both edge render passes.
src/DiagramForge/DiagramRenderer.cs Registers WireframeDslParser in the default parser set.
tests/DiagramForge.Tests/Parsers/WireframeDslParserTests.cs Adds comprehensive unit tests for wireframe parsing + render smoke tests.
tests/DiagramForge.E2ETests/Fixtures/wireframe-*.input/.expected.svg Adds wireframe snapshot fixtures/baselines.
doc/prd.md Documents Wireframe DSL scope and selection guidance.
README.md Documents Wireframe DSL and adds gallery entries/examples.
scripts/Build-Gallery.ps1 Updates gallery labeling to handle wireframe fixtures.

Comment on lines +828 to +831
private static void AppendWireframeNode(StringBuilder sb, Node node, string kind, Theme theme)
{
var p = WireframePalette.FromTheme(theme);

Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

WireframePalette.FromTheme(theme) is recomputed for every wireframe node render. Since it performs many ColorUtils.Blend calls, this can add noticeable overhead for larger wireframes. Consider computing the palette once per render/theme and reusing it across nodes (e.g., cache by Theme instance, or thread it through from the renderer).

Copilot uses AI. Check for mistakes.
if (diagram.Nodes.Count == 0)
return;

if (!diagram.Nodes.TryGetValue(Parsers.Wireframe.WireframeDslParser.RootNodeId, out var root))
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

RootNodeId is referenced as Parsers.Wireframe.WireframeDslParser.RootNodeId, but there is no Parsers namespace in scope here. This won’t compile unless you fully-qualify it (e.g., DiagramForge.Parsers.Wireframe.WireframeDslParser.RootNodeId) or add a using DiagramForge.Parsers.Wireframe; and reference WireframeDslParser.RootNodeId.

Suggested change
if (!diagram.Nodes.TryGetValue(Parsers.Wireframe.WireframeDslParser.RootNodeId, out var root))
if (!diagram.Nodes.TryGetValue(DiagramForge.Parsers.Wireframe.WireframeDslParser.RootNodeId, out var root))

Copilot uses AI. Check for mistakes.

badgeLabel = line[2..closeIdx].Trim();
trailingText = line[(closeIdx + 2)..].Trim();
return badgeLabel.Length > 0;
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

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

TrySplitInlineBadge will treat a standalone badge with trailing whitespace (e.g., "(( Info )) ") as an inline badge row, creating an extra implicit row and changing layout. After trimming the substring, consider returning false when trailingText is empty/whitespace so standalone badges remain standalone even with trailing spaces.

Suggested change
return badgeLabel.Length > 0;
// After trimming, require non-empty trailing text so standalone badges
// with only whitespace after the closing delimiters are not treated as inline.
return badgeLabel.Length > 0 && trailingText.Length > 0;

Copilot uses AI. Check for mistakes.
@jongalloway
Copy link
Copy Markdown
Owner

@copilot apply changes based on the comments in this thread

…fix inline badge whitespace edge case

Co-authored-by: jongalloway <68539+jongalloway@users.noreply.github.com>
Agent-Logs-Url: https://github.com/jongalloway/DiagramForge/sessions/04fdb593-36fa-43ae-a853-641cef1743a0
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 23, 2026

@copilot apply changes based on the comments in this thread

Applied all three changes in commit a7c207b:

  1. WireframePalette per-theme caching — Changed WireframePalette from readonly record struct to sealed record (class) and added a static ConditionalWeakTable<Theme, WireframePalette> s_paletteCache. AppendWireframeNode now uses s_paletteCache.GetValue(theme, ...) so the ~20 ColorUtils.Blend calls in FromTheme run at most once per distinct Theme instance per process, with entries automatically GC'd when the theme is collected.

  2. Namespace fix in DefaultLayoutEngine.Wireframe.cs — Added using DiagramForge.Parsers.Wireframe; and changed the reference from Parsers.Wireframe.WireframeDslParser.RootNodeId to WireframeDslParser.RootNodeId.

  3. TrySplitInlineBadge trailing-whitespace fix — Changed the final return to return badgeLabel.Length > 0 && trailingText.Length > 0 so "(( Badge )) " (badge + only whitespace) is no longer treated as an inline row and correctly falls through to ParseParenComponent as a standalone badge. Added a test case Parse_StandaloneBadge_WithTrailingWhitespace_IsNotTreatedAsInline to cover this.

@jongalloway jongalloway merged commit 79592e9 into main Mar 23, 2026
5 checks passed
@jongalloway jongalloway deleted the copilot/add-wireframe-dsl-parser branch March 23, 2026 20:41
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.

Explore: WireframeDslParser for markdown-ui-dsl wireframe diagrams

3 participants