You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat: add tables, task lists, images, alerts and syntax highlighting to Markdown - #37
Extends ShinyPDF.Markdown with the remaining items from #33:
Tables: pipe tables with a bold header that repeats on every page, plus column alignment (:--, :-:, --:).
Task lists: - [ ] / - [x] show a checkbox instead of the bullet.
Images: data: URIs render directly. Other URLs go through the new options.ImageResolver. By default Markdown never reads files or the network. Images that are missing or can't be decoded fall back to their alt text.
HTML entities: decoded (covered by tests). Raw HTML is still shown as plain text.
Alerts: > [!NOTE], TIP, IMPORTANT, WARNING, CAUTION, with titles and colors configurable through options.AlertStyles.
Syntax highlighting: regex-based, with built-in rules for C#, JS/TS, JSON, XML/HTML, CSS, SQL, Python, Shell, PowerShell and YAML. These are available as templates in SyntaxLanguages. Custom languages are added via new SyntaxLanguage(...).Rule(...), colors are set through options.SyntaxColors.
Mermaid / LaTeX: no built-in renderer. The new options.CodeBlockRenderers["mermaid"] = (container, code) => ... hook lets consumers plug in their own rendering.
Behavior change: pipe tables and task lists were plain text before and now render as tables and checkboxes. No public API is removed or changed.
Verification
dotnet build src/ShinyPDF.slnx: 0 errors
dotnet test src/ShinyPDF.UnitTests: 215 passed (30 new Markdown tests)
MarkdownExtendedFeatures example rendered and checked visually
…to Markdown
- Pipe tables with bold repeating header and column alignment
- Task list checkboxes
- Images from data: URIs or a user-provided ImageResolver (no file or
network access by default), alt text as fallback
- GitHub alerts with configurable titles and colors
- Regex-based syntax highlighting with built-in languages, extensible
through SyntaxLanguage and SyntaxLanguages templates
- CodeBlockRenderers hook for custom rendering such as Mermaid or math
Refs #33
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hlighting
- Align each text line in centered and right-aligned table columns
- Fully decode images before rendering; incomplete data falls back to alt text
- Keep the enclosing link on rendered images
- Bound image height with MaxImageHeight so tall images fit on a page
- Highlight full unquoted shell variable names
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Explicit left-aligned columns (:--) map to null, and ComposeText never calls AlignLeft(). Under ContentFromRightToLeft(), a null alignment defaults to right (src/ShinyPDF/Elements/Text/TextBlock.cs:56-63), so these columns ignore their Markdown alignment. Map TableColumnAlign.Left explicitly and apply text.AlignLeft(). Update the tests that expect null for these cells and add a right-to-left rendering case.
Treat empty keyword and type lists as no-ops
src/ShinyPDF.Markdown/SyntaxLanguage.cs:53
An empty Keywords() or Types() list registers a zero-width regex instead of doing nothing. If a string rule is added afterward, the empty rule can win at an opening quote; Tokenize skips that match, and regex iteration advances past the quote before the string rule can match it. Treat an empty word list as a no-op so it cannot suppress later highlighting rules.
Preserve capture numbering across highlighting rules
src/ShinyPDF.Markdown/SyntaxLanguage.cs:94
Combining independently validated patterns changes their numeric backreferences because capture numbering is shared. For example, (a)\1 and (b)\1 each match doubled letters alone, but the second rule's \1 now refers to the first rule's capture, so bb is not highlighted. Preserve each rule's capture semantics, for example by matching regexes independently and selecting the earliest match, with rule order breaking ties. Add a regression test with both rules.
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
- MaxImagePixels (default 40 MP) is checked from the image header before
any pixel buffer is allocated; larger images show their alt text
- SyntaxHighlightingTimeout (default 500 ms) bounds highlighting of one
code block, both per regex match and in total; the rest stays plain text
- Unterminated block comments and multi-line strings run to the end of
the code instead of rescanning the input for every opening marker
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Explicit left alignment (:--) falls through to null. In a document using ContentFromRightToLeft(), TextBlock.SetDefaultAlignment then chooses Right (src/ShinyPDF/Elements/Text/TextBlock.cs:56-63), contradicting the table's alignment marker. Map TableColumnAlign.Left to HorizontalAlignment.Left and handle it with text.AlignLeft() in ComposeText. Update the existing assertions that expect null for these cells and cover a right-to-left document.
Support percent-encoded non-base64 data URI images
src/ShinyPDF.Markdown/MarkdownRenderer.cs:463
The ;base64 requirement rejects valid percent-encoded data URIs, such as a PNG supplied as data:image/png,%89PNG.... These images always fall back to alt text despite the documented data URI support. Decode percent escapes directly to bytes for non-base64 payloads, without interpreting binary image bytes as UTF-8, and test both encoding forms.
Combined regexes break numbered backreferences
src/ShinyPDF.Markdown/SyntaxLanguage.cs:126
Combining rules into one regex changes numbered backreferences. After a (#).* rule, a string rule such as (['"]).*?\1 references the first rule's capture rather than its own quote, so valid quoted strings no longer match. Preserve each rule's capture scope, for example by compiling rules separately and selecting the earliest match with rule order breaking ties. Keep the timeout fallback and add a regression test with captures in multiple rules.
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
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
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.
Closes #33
What
Extends
ShinyPDF.Markdownwith the remaining items from #33::--,:-:,--:).- [ ]/- [x]show a checkbox instead of the bullet.data:URIs render directly. Other URLs go through the newoptions.ImageResolver. By default Markdown never reads files or the network. Images that are missing or can't be decoded fall back to their alt text.> [!NOTE],TIP,IMPORTANT,WARNING,CAUTION, with titles and colors configurable throughoptions.AlertStyles.SyntaxLanguages. Custom languages are added vianew SyntaxLanguage(...).Rule(...), colors are set throughoptions.SyntaxColors.options.CodeBlockRenderers["mermaid"] = (container, code) => ...hook lets consumers plug in their own rendering.Behavior change: pipe tables and task lists were plain text before and now render as tables and checkboxes. No public API is removed or changed.
Verification
dotnet build src/ShinyPDF.slnx: 0 errorsdotnet test src/ShinyPDF.UnitTests: 215 passed (30 new Markdown tests)MarkdownExtendedFeaturesexample rendered and checked visuallydotnet packfor ShinyPDF.Markdown succeeds🤖 Generated with Claude Code