Skip to content

Add offline BPMN, PlantUML, and SVG diagrams - #30

Closed
arvearve wants to merge 2 commits into
b451c:mainfrom
arvearve:feature/offline-diagram-renderers
Closed

Add offline BPMN, PlantUML, and SVG diagrams#30
arvearve wants to merge 2 commits into
b451c:mainfrom
arvearve:feature/offline-diagram-renderers

Conversation

@arvearve

@arvearve arvearve commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Documents can now render fenced BPMN 2.0 XML, PlantUML (including the puml alias), and SVG alongside Mermaid. Markdown image links to .bpmn, .puml, .plantuml, and .svg files use the same rendering, text scaling, and window-filling vector previews.

Bundled JavaScript libraries run offline in a shared, serialized WebKit renderer with cached SVG output. Fenced diagrams also render in PDF export and print. Includes reproducible vendor tooling and licenses, BPMN attribution, fixtures, and a sample document.

Depends on #29 for graphics scaling and preview presentation. This branch currently includes that PR's commit; the diagram feature itself is commit 91c93fb. Merge #29 first.

Validation: full Debug test suite passed, including real WebKit rendering, linked-format fixtures, error recovery, inert SVG display, and vector PDF output. Native interaction with this feature still needs a manual smoke test. Linked diagram PDF/print support is not included; export rendering covers fenced diagrams.

@b451c

b451c commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Thank you for the work that went into this - the shared, serialized WebKit renderer, the vendor tooling with licenses, the fixtures and the export path are all carefully done, and #29 (which this builds on) is merged.

I am not going to merge this one, and I want to be straight about why rather than let it sit. QuickMD's positioning is "lightweight, native, zero dependencies" (README, and the project's own rule of thumb: no third-party code, well under 5% binary growth per feature). This branch vendors about 11.5 MB of JavaScript (plantuml.js 7.6 MB, emoji.js 1.8 MB, viz-global.js 1.4 MB, plus bpmn-js) into an app that is about 10 MB today, and adds three JS engines that have to be kept patched and rendered in WebKit. That is a different product from the one the README promises, so it has to be a no from the maintainers' side regardless of the quality of the implementation. People who need BPMN or PlantUML in their documents usually keep a rendered .svg next to the source, and that path already works in QuickMD (see below).

The part I would like to keep: SVG. I checked today that ![](diagram.svg) already renders natively on main through NSImage (CoreSVG, no WebKit, scripts ignored), and it gets #29's scaling and click-to-enlarge for free. What is missing is fenced ```svg blocks, and that can be done the same way - hand the block's text to NSImage as data, measure it like an image, render it in PDF and print through the existing image path. Zero dependencies, a small diff. If you would like to split exactly that out of this branch into its own PR, it is very welcome and I will review it quickly; if not, it is on my list and I will credit the idea here.

Closing this PR with the reasoning above; the branch stays in your fork for anyone who wants offline BPMN/PlantUML.

@b451c b451c closed this Sep 10, 2026
b451c added a commit that referenced this pull request Sep 10, 2026
The SVG half of #30 without the WebKit renderer or any vendored library:
the block's markup is handed to NSImage as data and CoreSVG draws it
(`public.svg-image` is a supported NSImage type on every release we
target; scripts and external references in the markup are ignored by the
decoder, non-SVG text yields nil).

- Parser: a ```svg fence (case-insensitive, like ```mermaid) becomes
  `.svgImage(source:)` with id "svg-N".
- `Views/SVGBlockView.swift`: decodes off-main, shows the image scaled with
  the zoom through `BlockLayout.ImageBlock.displayWidth` (never wider than
  the column) but, unlike a bitmap, capped at its own declared size × zoom
  so icons stay icon-sized; click opens the #29 window-filling preview;
  undecodable markup shows a notice.
- `SVGImageDecoder.swift`: the one decode shared by the block and by
  `PrintableSVGView` (print / PDF: a plain SwiftUI `Image`, so ImageRenderer
  draws it as a vector; garbage degrades to the styled code block like a
  failed Mermaid render). Kept out of the view file so the test target,
  which compiles model files only, can exercise it.
- Measurer: `.reported` row seeded like `.image`; anchor signature "s|";
  search skips SVG markup (a hit inside a path could be neither shown nor
  highlighted).
- Tests: parser (svg / SVG fence → block, verbatim markup), decoder
  (declared size, viewBox-only, size-less, inert script, garbage, HTML),
  measurer fixture and kind checks include the new block. 195 tests.
- README (Inline SVG feature line, tree, feature list), CHANGELOG (Added,
  suggested by @arvearve in #30).

Verified in the app (Debug, AX-driven): declared 320×120 at 100 % → 400×150
at 125 %; a 1600-wide viewBox capped at 600 → column width at 125 %; a 24 pt
icon → 30 pt; garbage → notice; linked .svg still renders; preview opens
vector-sharp; PDF export shows the blocks as images and the garbage block
as code.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017oPbKHjsi1HB3RWgxxFTKz
@b451c

b451c commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Follow-up: the SVG part is on main now (28a7ef7). Fenced ```svg blocks are decoded natively by NSImage (CoreSVG) - no web view, no bundled code - and get the same zoom scaling and click-to-enlarge preview as images and Mermaid, plus vector output in PDF and print. You are credited in the CHANGELOG for the suggestion. Thanks again.

@b451c

b451c commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Correction to my size numbers above, after measuring the actual artifacts rather than quoting from memory: the 1.9.1 app bundle is 17 MB on disk (math fonts 7.1 MB, binary 3.6 MB, mermaid.min.js 3.0 MB, asset catalog 2.8 MB); the "about 10 MB" I wrote is the compressed download (9.78 MB zip). The vendored files in this branch are 11.5 MB on disk (plantuml.js 7.6 MB, emoji.js 1.8 MB, viz-global.js 1.4 MB, themes.js 0.3 MB, bpmn-viewer.js 0.2 MB), so the like-for-like comparison is +11.5 MB on a 17 MB bundle, about +67 percent, not "more than doubling". The decision does not change, but the numbers should be right.

@b451c

b451c commented Sep 10, 2026

Copy link
Copy Markdown
Owner

The native SVG blocks shipped in v1.10.0: https://github.com/b451c/quickmd/releases/tag/v1.10.0 - credited in the CHANGELOG.

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.

2 participants