From 37de6b56eb11bf712d81700074cfaafa57fffdb7 Mon Sep 17 00:00:00 2001 From: TsunamiNoAi Date: Sun, 19 Apr 2026 15:51:19 -0400 Subject: [PATCH 1/6] Integrate Mermaid.js rendering with Pozeiden support in HTML and Typst outputs --- build.zig | 5 +++ build.zig.zon | 3 ++ build.zig.zon2json-lock | 5 +++ src/main.zig | 5 +-- src/markdown/renderers/html.zig | 58 ++++++++++++++++++++++++-------- src/markdown/renderers/typst.zig | 43 ++++++++++++++++++++++- src/root.zig | 29 ++++++++++++++++ 7 files changed, 131 insertions(+), 17 deletions(-) diff --git a/build.zig b/build.zig index 7b07826..889a336 100644 --- a/build.zig +++ b/build.zig @@ -35,6 +35,10 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, }); + const pozeiden_dep = b.dependency("pozeiden", .{ + .target = target, + .optimize = optimize, + }); const options = b.addOptions(); // Version priority: -Dversion flag > git describe > build.zig.zon // The flag lets Nix (and other sandboxed builds) inject the version @@ -100,6 +104,7 @@ pub fn build(b: *std.Build) void { .imports = &.{ .{ .name = "zigmark", .module = zigmark }, .{ .name = "clap", .module = clap_dep.module("clap") }, + .{ .name = "pozeiden", .module = pozeiden_dep.module("pozeiden") }, }, }), }); diff --git a/build.zig.zon b/build.zig.zon index b174475..1f30f3c 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -66,6 +66,9 @@ .url = "git+https://github.com/github/cmark-gfm.git#587a12bb54d95ac37241377e6ddc93ea0e45439b", .hash = "N-V-__8AACaGFwDmjLKFjd0jlhijuVlTuOCGoRVvp406CsLD", }, + .pozeiden = .{ + .path = "../pozeiden", + }, }, // Specifies the set of files and directories that are included in this package. // Only files and directories listed here are included in the `hash` that diff --git a/build.zig.zon2json-lock b/build.zig.zon2json-lock index 9fe4186..16decf3 100644 --- a/build.zig.zon2json-lock +++ b/build.zig.zon2json-lock @@ -44,5 +44,10 @@ "url": "git+https://github.com/github/cmark-gfm.git#587a12bb54d95ac37241377e6ddc93ea0e45439b", "hash": "sha256-HiSGtRsSbW03R6aKoMVVFOLrwP5aXtpeXUC/bE5M/qo=", "rev": "587a12bb54d95ac37241377e6ddc93ea0e45439b" + }, + "zigmark-0.5.0-cwOiyHzQCQBz8SXqL4pV4shRF5KG4kZxvbps1fpHygGg": { + "name": "zigmark", + "url": "https://github.com/sc2in/zigmark/archive/refs/tags/v0.5.2.tar.gz", + "hash": "sha256-7mNLq+AyaWVCFF4WyYkiahTGhwSgQTYBW2gCCkL/G2E=" } } \ No newline at end of file diff --git a/src/main.zig b/src/main.zig index 0aa0caa..f8949cb 100644 --- a/src/main.zig +++ b/src/main.zig @@ -9,6 +9,7 @@ pub const std_options: std.Options = .{ const clap = @import("clap"); const zigmark = @import("zigmark"); +const pozeiden = @import("pozeiden"); const AST = zigmark.AST; const version = zigmark.version; @@ -267,7 +268,7 @@ pub fn main() !void { // ── HTML ───────────────────────────────────────────────────────────────── if (std.mem.eql(u8, format, "html")) { - zigmark.HTMLRenderer.renderToWriter(alloc, &writer.interface, doc) catch |err| { + zigmark.renderHtmlWithMermaid(alloc, &writer.interface, doc, &pozeiden.render) catch |err| { std.debug.print("error: failed to render HTML: {}\n", .{err}); return err; }; @@ -302,7 +303,7 @@ pub fn main() !void { frontmatterToTypstOpts(f) else .{}; - zigmark.typst.renderDocumentToWriter(alloc, &writer.interface, doc, opts) catch |err| { + zigmark.renderTypstDocWithMermaid(alloc, &writer.interface, doc, opts, &pozeiden.render) catch |err| { std.debug.print("error: failed to render Typst: {}\n", .{err}); return err; }; diff --git a/src/markdown/renderers/html.zig b/src/markdown/renderers/html.zig index 9c24bac..daa1bb0 100644 --- a/src/markdown/renderers/html.zig +++ b/src/markdown/renderers/html.zig @@ -552,7 +552,13 @@ fn renderInline(writer: anytype, item: AST.Inline, gfm: bool) !void { // ── Block renderer ──────────────────────────────────────────────────────────── -fn renderBlock(writer: *std.Io.Writer, block: AST.Block, gfm: bool) !void { +const RenderCtx = struct { + gfm: bool, + allocator: Allocator, + mermaid: ?*const fn (Allocator, []const u8) anyerror![]const u8 = null, +}; + +fn renderBlock(writer: *std.Io.Writer, block: AST.Block, ctx: *const RenderCtx) !void { switch (block) { .table => |tbl| { try writer.writeAll("\n"); @@ -566,7 +572,7 @@ fn renderBlock(writer: *std.Io.Writer, block: AST.Block, gfm: bool) !void { .center => try writer.writeAll("\n"); } try writer.writeAll("\n\n"); @@ -583,7 +589,7 @@ fn renderBlock(writer: *std.Io.Writer, block: AST.Block, gfm: bool) !void { .center => try writer.writeAll("\n"); } try writer.writeAll("\n"); @@ -596,12 +602,12 @@ fn renderBlock(writer: *std.Io.Writer, block: AST.Block, gfm: bool) !void { .heading => |h| { try writer.print("", .{h.level}); - for (h.children.items) |item| try renderInline(writer, item, gfm); + for (h.children.items) |item| try renderInline(writer, item, ctx.gfm); try writer.print("\n", .{h.level}); }, .paragraph => |p| { try writer.writeAll("

"); - for (p.children.items) |item| try renderInline(writer, item, gfm); + for (p.children.items) |item| try renderInline(writer, item, ctx.gfm); try writer.writeAll("

\n"); }, .thematic_break => try writer.writeAll("
\n"), @@ -611,6 +617,18 @@ fn renderBlock(writer: *std.Io.Writer, block: AST.Block, gfm: bool) !void { try writer.writeAll("\n\n"); }, .fenced_code_block => |fcb| { + mermaid: { + if (ctx.mermaid) |mfn| { + const is_mermaid = if (fcb.language) |l| std.mem.eql(u8, l, "mermaid") else false; + if (!is_mermaid) break :mermaid; + const svg = mfn(ctx.allocator, fcb.content) catch break :mermaid; + defer ctx.allocator.free(svg); + try writer.writeAll("
\n"); + try writer.writeAll(svg); + try writer.writeAll("
\n"); + return; + } + } if (fcb.language) |lang| { try writer.writeAll("
 |bq| {
             try writer.writeAll("
\n"); - for (bq.children.items) |child| try renderBlock(writer, child, gfm); + for (bq.children.items) |child| try renderBlock(writer, child, ctx); try writer.writeAll("
\n"); }, .list => |lst| { @@ -671,7 +689,7 @@ fn renderBlock(writer: *std.Io.Writer, block: AST.Block, gfm: bool) !void { for (item.children.items) |child| { switch (child) { .paragraph => |p| { - for (p.children.items) |inl| try renderInline(writer, inl, gfm); + for (p.children.items) |inl| try renderInline(writer, inl, ctx.gfm); wrote_inline = true; }, else => { @@ -680,7 +698,7 @@ fn renderBlock(writer: *std.Io.Writer, block: AST.Block, gfm: bool) !void { try writer.writeByte('\n'); } wrote_inline = false; - try renderBlock(writer, child, gfm); + try renderBlock(writer, child, ctx); }, } } @@ -691,7 +709,7 @@ fn renderBlock(writer: *std.Io.Writer, block: AST.Block, gfm: bool) !void { try writer.writeAll("
  • "); } else { try writer.writeAll("
  • \n"); - for (item.children.items) |child| try renderBlock(writer, child, gfm); + for (item.children.items) |child| try renderBlock(writer, child, ctx); } try writer.writeAll("
  • \n"); } @@ -704,15 +722,15 @@ fn renderBlock(writer: *std.Io.Writer, block: AST.Block, gfm: bool) !void { switch (child) { .paragraph => |p| { try writer.print("

    {s}: ", .{fd.label}); - for (p.children.items) |inl| try renderInline(writer, inl, gfm); + for (p.children.items) |inl| try renderInline(writer, inl, ctx.gfm); try writer.writeAll("

    \n"); }, - else => try renderBlock(writer, child, gfm), + else => try renderBlock(writer, child, ctx), } } try writer.writeAll("\n"); }, - .html_block => |hb| try writeHtmlFiltered(writer, hb.content, gfm), + .html_block => |hb| try writeHtmlFiltered(writer, hb.content, ctx.gfm), } } @@ -720,8 +738,20 @@ fn renderBlock(writer: *std.Io.Writer, block: AST.Block, gfm: bool) !void { /// Render `doc` to a writer with CommonMark-compliant HTML. pub fn renderToWriter(allocator: Allocator, writer: *std.Io.Writer, doc: AST.Document) !void { - _ = allocator; - for (doc.children.items) |child| try renderBlock(writer, child, doc.gfm); + const ctx: RenderCtx = .{ .gfm = doc.gfm, .allocator = allocator }; + for (doc.children.items) |child| try renderBlock(writer, child, &ctx); +} + +/// Render `doc` to a writer, converting mermaid fenced blocks to inline SVG +/// using the provided renderer function. +pub fn renderToWriterWithMermaid( + allocator: Allocator, + writer: *std.Io.Writer, + doc: AST.Document, + mermaid: ?*const fn (Allocator, []const u8) anyerror![]const u8, +) !void { + const ctx: RenderCtx = .{ .gfm = doc.gfm, .allocator = allocator, .mermaid = mermaid }; + for (doc.children.items) |child| try renderBlock(writer, child, &ctx); } /// Render `doc` to an allocator-owned HTML byte slice. diff --git a/src/markdown/renderers/typst.zig b/src/markdown/renderers/typst.zig index c5990b9..243319c 100644 --- a/src/markdown/renderers/typst.zig +++ b/src/markdown/renderers/typst.zig @@ -127,9 +127,14 @@ fn writeStringLiteral(writer: anytype, s: []const u8) !void { const Ctx = struct { /// label → definition node (borrowed references; lifetime == the AST). footnotes: std.StringHashMap(*const AST.FootnoteDefinition), + allocator: Allocator, + mermaid: ?*const fn (Allocator, []const u8) anyerror![]const u8 = null, fn init(allocator: Allocator) Ctx { - return .{ .footnotes = std.StringHashMap(*const AST.FootnoteDefinition).init(allocator) }; + return .{ + .footnotes = std.StringHashMap(*const AST.FootnoteDefinition).init(allocator), + .allocator = allocator, + }; } fn deinit(self: *Ctx) void { @@ -284,6 +289,22 @@ fn renderBlock(writer: *std.Io.Writer, block: AST.Block, ctx: *const Ctx) anyerr }, .fenced_code_block => |fcb| { + mermaid: { + if (ctx.mermaid) |mfn| { + const is_mermaid = if (fcb.language) |l| std.mem.eql(u8, l, "mermaid") else false; + if (!is_mermaid) break :mermaid; + const svg = mfn(ctx.allocator, fcb.content) catch break :mermaid; + defer ctx.allocator.free(svg); + const encoded_len = std.base64.standard.Encoder.calcSize(svg.len); + const encoded = try ctx.allocator.alloc(u8, encoded_len); + defer ctx.allocator.free(encoded); + _ = std.base64.standard.Encoder.encode(encoded, svg); + try writer.writeAll("#image.decode(bytes.fromBase64(\""); + try writer.writeAll(encoded); + try writer.writeAll("\"), format: \"svg\", width: 100%)\n\n"); + return; + } + } try writer.writeAll("```"); if (fcb.language) |lang| try writer.writeAll(lang); try writer.writeByte('\n'); @@ -727,6 +748,26 @@ pub fn renderDocumentToWriter(allocator: Allocator, writer: *std.Io.Writer, doc: } } +/// Render `doc` to a writer as a complete Typst document, converting mermaid +/// fenced blocks to `#image.decode` calls using the provided renderer. +pub fn renderDocumentToWriterWithMermaid( + allocator: Allocator, + writer: *std.Io.Writer, + doc: AST.Document, + opts: DocumentOptions, + mermaid: ?*const fn (Allocator, []const u8) anyerror![]const u8, +) !void { + var ctx = Ctx.init(allocator); + ctx.mermaid = mermaid; + defer ctx.deinit(); + try collectFootnotes(doc, &ctx); + try writePreamble(writer, opts); + for (doc.children.items) |child| { + if (child == .footnote_definition) continue; + try renderBlock(writer, child, &ctx); + } +} + /// Render `doc` as a complete Typst document with an Eisvogel-inspired /// preamble derived from `opts`. /// diff --git a/src/root.zig b/src/root.zig index cdda652..1ec0ac9 100644 --- a/src/root.zig +++ b/src/root.zig @@ -67,6 +67,35 @@ pub const TypstRenderer = Renderer.create(typst_mod); /// the Eisvogel-inspired preamble with title page, header/footer, etc. pub const typst = typst_mod; +/// Function pointer type for an optional mermaid diagram renderer. +/// Pass a function matching this signature to `renderHtmlWithMermaid` or +/// `renderTypstDocWithMermaid` to have mermaid fenced code blocks converted +/// to inline diagrams instead of emitting raw code blocks. +pub const MermaidRendererFn = *const fn (std.mem.Allocator, []const u8) anyerror![]const u8; + +/// Render `doc` to HTML, converting mermaid fenced blocks to inline SVG +/// `
    ` elements using `mermaid` (pass `null` to skip conversion). +pub fn renderHtmlWithMermaid( + allocator: std.mem.Allocator, + writer: *std.Io.Writer, + doc: AST.Document, + mermaid: ?MermaidRendererFn, +) !void { + return html.renderToWriterWithMermaid(allocator, writer, doc, mermaid); +} + +/// Render `doc` to a full Typst document, converting mermaid fenced blocks +/// to `#image.decode` calls using `mermaid` (pass `null` to skip conversion). +pub fn renderTypstDocWithMermaid( + allocator: std.mem.Allocator, + writer: *std.Io.Writer, + doc: AST.Document, + opts: typst_mod.DocumentOptions, + mermaid: ?MermaidRendererFn, +) !void { + return typst_mod.renderDocumentToWriterWithMermaid(allocator, writer, doc, opts, mermaid); +} + /// A type-erased rendering back-end. /// /// Create concrete instances with `Renderer.create`, passing any struct that From bbebc0fd3d72d7574005aad969262ccf08d45b7c Mon Sep 17 00:00:00 2001 From: TsunamiNoAi Date: Sun, 19 Apr 2026 15:55:16 -0400 Subject: [PATCH 2/6] Enhance Mermaid.js support by recognizing "mermaidjs" as a valid language in HTML and Typst renderers --- src/markdown/renderers/html.zig | 2 +- src/markdown/renderers/typst.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/markdown/renderers/html.zig b/src/markdown/renderers/html.zig index daa1bb0..6a297a6 100644 --- a/src/markdown/renderers/html.zig +++ b/src/markdown/renderers/html.zig @@ -619,7 +619,7 @@ fn renderBlock(writer: *std.Io.Writer, block: AST.Block, ctx: *const RenderCtx) .fenced_code_block => |fcb| { mermaid: { if (ctx.mermaid) |mfn| { - const is_mermaid = if (fcb.language) |l| std.mem.eql(u8, l, "mermaid") else false; + const is_mermaid = if (fcb.language) |l| std.mem.eql(u8, l, "mermaid") or std.mem.eql(u8, l, "mermaidjs") else false; if (!is_mermaid) break :mermaid; const svg = mfn(ctx.allocator, fcb.content) catch break :mermaid; defer ctx.allocator.free(svg); diff --git a/src/markdown/renderers/typst.zig b/src/markdown/renderers/typst.zig index 243319c..b54add5 100644 --- a/src/markdown/renderers/typst.zig +++ b/src/markdown/renderers/typst.zig @@ -291,7 +291,7 @@ fn renderBlock(writer: *std.Io.Writer, block: AST.Block, ctx: *const Ctx) anyerr .fenced_code_block => |fcb| { mermaid: { if (ctx.mermaid) |mfn| { - const is_mermaid = if (fcb.language) |l| std.mem.eql(u8, l, "mermaid") else false; + const is_mermaid = if (fcb.language) |l| std.mem.eql(u8, l, "mermaid") or std.mem.eql(u8, l, "mermaidjs") else false; if (!is_mermaid) break :mermaid; const svg = mfn(ctx.allocator, fcb.content) catch break :mermaid; defer ctx.allocator.free(svg); From 669bc7117571269fde013d2c2e3be08f915947d6 Mon Sep 17 00:00:00 2001 From: TsunamiNoAi Date: Sun, 19 Apr 2026 17:56:05 -0400 Subject: [PATCH 3/6] Integrate Pozeiden support for Mermaid.js rendering and add fallback mechanisms --- build.zig | 10 ++++- build.zig.zon | 3 +- build.zig.zon2json-lock | 5 +++ flake.nix | 8 ++-- src/markdown/renderers/html.zig | 62 ++++++++++++++++++++++++++++++ src/markdown/renderers/typst.zig | 65 ++++++++++++++++++++++++++++++++ src/noop_mermaid.zig | 8 ++++ 7 files changed, 154 insertions(+), 7 deletions(-) create mode 100644 src/noop_mermaid.zig diff --git a/build.zig b/build.zig index 889a336..3d3537a 100644 --- a/build.zig +++ b/build.zig @@ -35,10 +35,16 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, }); - const pozeiden_dep = b.dependency("pozeiden", .{ + const pozeiden_dep = b.lazyDependency("pozeiden", .{ .target = target, .optimize = optimize, }); + const pozeiden_module = if (pozeiden_dep) |dep| + dep.module("pozeiden") + else + b.addModule("pozeiden-stub", .{ + .root_source_file = b.path("src/noop_mermaid.zig"), + }); const options = b.addOptions(); // Version priority: -Dversion flag > git describe > build.zig.zon // The flag lets Nix (and other sandboxed builds) inject the version @@ -104,7 +110,7 @@ pub fn build(b: *std.Build) void { .imports = &.{ .{ .name = "zigmark", .module = zigmark }, .{ .name = "clap", .module = clap_dep.module("clap") }, - .{ .name = "pozeiden", .module = pozeiden_dep.module("pozeiden") }, + .{ .name = "pozeiden", .module = pozeiden_module }, }, }), }); diff --git a/build.zig.zon b/build.zig.zon index 1f30f3c..7142c05 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -67,7 +67,8 @@ .hash = "N-V-__8AACaGFwDmjLKFjd0jlhijuVlTuOCGoRVvp406CsLD", }, .pozeiden = .{ - .path = "../pozeiden", + .url = "https://github.com/sc2in/pozeiden/archive/refs/tags/v0.1.0.tar.gz", + .hash = "pozeiden-0.1.0-NAqiXThiCgCKuU61t_p5Nw2YP7ZHK6JMFfGcMXIX3HLN", }, }, // Specifies the set of files and directories that are included in this package. diff --git a/build.zig.zon2json-lock b/build.zig.zon2json-lock index 16decf3..492de67 100644 --- a/build.zig.zon2json-lock +++ b/build.zig.zon2json-lock @@ -45,6 +45,11 @@ "hash": "sha256-HiSGtRsSbW03R6aKoMVVFOLrwP5aXtpeXUC/bE5M/qo=", "rev": "587a12bb54d95ac37241377e6ddc93ea0e45439b" }, + "pozeiden-0.1.0-NAqiXThiCgCKuU61t_p5Nw2YP7ZHK6JMFfGcMXIX3HLN": { + "name": "pozeiden", + "url": "https://github.com/sc2in/pozeiden/archive/refs/tags/v0.1.0.tar.gz", + "hash": "sha256-FGno4AH5nbh4+QiiUy97J8vyo0oes5JhT/+JQqAAUdA=" + }, "zigmark-0.5.0-cwOiyHzQCQBz8SXqL4pV4shRF5KG4kZxvbps1fpHygGg": { "name": "zigmark", "url": "https://github.com/sc2in/zigmark/archive/refs/tags/v0.5.2.tar.gz", diff --git a/flake.nix b/flake.nix index 7069e61..e2b4a6f 100644 --- a/flake.nix +++ b/flake.nix @@ -110,9 +110,9 @@ echo "zig is not installed or not in PATH" >&2 exit 1 fi - echo "Updating build.zig.zon dependencies..." - zig fetch --save . - echo "build.zig.zon updated." + echo "Regenerating build.zig.zon2json-lock..." + env -u ZIG_GLOBAL_CACHE_DIR zig2nix zon2lock + echo "build.zig.zon2json-lock updated." '') benchmark ]; @@ -126,7 +126,7 @@ if [ -f build.zig.zon ]; then if [ ! -f build.zig.zon2json-lock ] || [ build.zig.zon -nt build.zig.zon2json-lock ]; then echo "zig2nix: regenerating build.zig.zon2json-lock..." - zig2nix zon2lock + env -u ZIG_GLOBAL_CACHE_DIR zig2nix zon2lock fi fi ''; diff --git a/src/markdown/renderers/html.zig b/src/markdown/renderers/html.zig index 6a297a6..b35d1e8 100644 --- a/src/markdown/renderers/html.zig +++ b/src/markdown/renderers/html.zig @@ -1063,3 +1063,65 @@ test "gfm table basic" { "
    "), .right => try writer.writeAll(""), } - for (cell.children.items) |inl| try renderInline(writer, inl, gfm); + for (cell.children.items) |inl| try renderInline(writer, inl, ctx.gfm); try writer.writeAll("
    "), .right => try writer.writeAll(""), } - for (cell.children.items) |inl| try renderInline(writer, inl, gfm); + for (cell.children.items) |inl| try renderInline(writer, inl, ctx.gfm); try writer.writeAll("
    \n", ); } + +fn okMermaid(src: []const u8, mfn: ?*const fn (std.mem.Allocator, []const u8) anyerror![]const u8, expected: []const u8) !void { + const allocator = tst.allocator; + var parser = Parser.init(); + defer parser.deinit(allocator); + var res = try parser.parseMarkdown(allocator, src); + defer res.deinit(allocator); + var aw: std.Io.Writer.Allocating = .init(allocator); + defer aw.deinit(); + try renderToWriterWithMermaid(allocator, &aw.writer, res, mfn); + const out = try aw.toOwnedSlice(); + defer allocator.free(out); + try tst.expectEqualStrings(expected, out); +} + +fn stubSvg(alloc: std.mem.Allocator, _: []const u8) anyerror![]const u8 { + return alloc.dupe(u8, "mock"); +} + +fn stubSvgError(_: std.mem.Allocator, _: []const u8) anyerror![]const u8 { + return error.RenderFailed; +} + +test "mermaid block renders as figure" { + try okMermaid( + "```mermaid\ngraph LR\nA-->B\n```", + stubSvg, + "
    \nmock
    \n", + ); +} + +test "mermaidjs block renders as figure" { + try okMermaid( + "```mermaidjs\ngraph LR\nA-->B\n```", + stubSvg, + "
    \nmock
    \n", + ); +} + +test "mermaid renderer error falls back to code block" { + try okMermaid( + "```mermaid\ngraph LR\nA-->B\n```", + stubSvgError, + "
    graph LR\nA-->B\n
    \n", + ); +} + +test "mermaid null renderer falls back to code block" { + try okMermaid( + "```mermaid\ngraph LR\nA-->B\n```", + null, + "
    graph LR\nA-->B\n
    \n", + ); +} + +test "non-mermaid lang unaffected by mermaid renderer" { + try okMermaid( + "```zig\nconst x = 1;\n```", + stubSvg, + "
    const x = 1;\n
    \n", + ); +} diff --git a/src/markdown/renderers/typst.zig b/src/markdown/renderers/typst.zig index b54add5..dc109d3 100644 --- a/src/markdown/renderers/typst.zig +++ b/src/markdown/renderers/typst.zig @@ -860,6 +860,71 @@ test "special char escaping" { try ok("a $ b", "a \\$ b\n\n"); } +fn okMermaid(src: []const u8, mfn: ?*const fn (std.mem.Allocator, []const u8) anyerror![]const u8, expected: []const u8) !void { + const allocator = tst.allocator; + var parser = Parser.init(); + defer parser.deinit(allocator); + var res = try parser.parseMarkdown(allocator, src); + defer res.deinit(allocator); + var ctx = Ctx.init(allocator); + ctx.mermaid = mfn; + defer ctx.deinit(); + var aw: std.Io.Writer.Allocating = .init(allocator); + defer aw.deinit(); + for (res.children.items) |child| try renderBlock(&aw.writer, child, &ctx); + const out = try aw.toOwnedSlice(); + defer allocator.free(out); + try tst.expectEqualStrings(expected, out); +} + +fn stubSvg(alloc: std.mem.Allocator, _: []const u8) anyerror![]const u8 { + return alloc.dupe(u8, "mock"); +} + +fn stubSvgError(_: std.mem.Allocator, _: []const u8) anyerror![]const u8 { + return error.RenderFailed; +} + +test "mermaid block renders as image.decode" { + try okMermaid( + "```mermaid\ngraph LR\nA-->B\n```", + stubSvg, + "#image.decode(bytes.fromBase64(\"PHN2Zz5tb2NrPC9zdmc+\"), format: \"svg\", width: 100%)\n\n", + ); +} + +test "mermaidjs block renders as image.decode" { + try okMermaid( + "```mermaidjs\ngraph LR\nA-->B\n```", + stubSvg, + "#image.decode(bytes.fromBase64(\"PHN2Zz5tb2NrPC9zdmc+\"), format: \"svg\", width: 100%)\n\n", + ); +} + +test "mermaid renderer error falls back to code block" { + try okMermaid( + "```mermaid\ngraph LR\nA-->B\n```", + stubSvgError, + "```mermaid\ngraph LR\nA-->B\n```\n\n", + ); +} + +test "mermaid null renderer falls back to code block" { + try okMermaid( + "```mermaid\ngraph LR\nA-->B\n```", + null, + "```mermaid\ngraph LR\nA-->B\n```\n\n", + ); +} + +test "non-mermaid lang unaffected by mermaid renderer" { + try okMermaid( + "```zig\nconst x = 1;\n```", + stubSvg, + "```zig\nconst x = 1;\n```\n\n", + ); +} + test "renderDocument smoke test" { const allocator = tst.allocator; var parser = Parser.init(); diff --git a/src/noop_mermaid.zig b/src/noop_mermaid.zig new file mode 100644 index 0000000..e8eede9 --- /dev/null +++ b/src/noop_mermaid.zig @@ -0,0 +1,8 @@ +const std = @import("std"); + +/// Stand-in for the pozeiden mermaid renderer used when the pozeiden +/// dependency is not available. Always returns an error so callers fall +/// back to rendering the raw fenced code block. +pub fn render(_: std.mem.Allocator, _: []const u8) anyerror![]const u8 { + return error.MermaidUnavailable; +} From b6712d78d2e826eed6190d09fe17287ef55ec023 Mon Sep 17 00:00:00 2001 From: TsunamiNoAi Date: Mon, 20 Apr 2026 11:52:46 -0400 Subject: [PATCH 4/6] Add GitHub Actions workflow for deploying documentation and site build process --- .github/workflows/deploy-docs.yml | 45 +++++++++++++++++++++++++++++++ build.zig | 16 +++++++++++ examples/wasm/index.html | 9 ++++++- flake.nix | 14 ++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/deploy-docs.yml diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 0000000..a2413e1 --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,45 @@ +name: Deploy Docs + +on: + push: + branches: ["main"] + workflow_dispatch: + +concurrency: + group: deploy + cancel-in-progress: true + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + deployments: write + steps: + - uses: actions/checkout@v4 + + - name: Install Nix + uses: DeterminateSystems/determinate-nix-action@main + with: + extra-conf: | + sandbox = false + + - name: Setup Nix cache + uses: DeterminateSystems/flakehub-cache-action@main + with: + use-gha-cache: "enabled" + flakehub-flake-name: "sc2in/zigmark" + + - name: Configure permissions + run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true + + - name: Build site + run: nix build .#site -o site-out + + - name: Deploy to Cloudflare Pages + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_SC2_PAGES_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy site-out --project-name=zigmark --commit-dirty=true diff --git a/build.zig b/build.zig index 3d3537a..7b9d45b 100644 --- a/build.zig +++ b/build.zig @@ -337,6 +337,22 @@ pub fn build(b: *std.Build) void { const install_html = b.addInstallFile(b.path("examples/wasm/index.html"), "wasm/index.html"); wasm_step.dependOn(&install_wasm.step); wasm_step.dependOn(&install_html.step); + + // ── Site build (playground + docs → zig-out/site/) ─────────────────────── + // Deployed to zigmark.sc2.in via nix build .#site + const site_step = b.step("site", "Build the combined site (playground + docs) for zigmark.sc2.in"); + const site_wasm = b.addInstallArtifact(wasm_lib, .{ + .dest_dir = .{ .override = .{ .custom = "site" } }, + }); + const site_html = b.addInstallFile(b.path("examples/wasm/index.html"), "site/index.html"); + const site_docs = b.addInstallDirectory(.{ + .source_dir = lib.getEmittedDocs(), + .install_dir = .prefix, + .install_subdir = "site/docs", + }); + site_step.dependOn(&site_wasm.step); + site_step.dependOn(&site_html.step); + site_step.dependOn(&site_docs.step); } /// Strip a leading "v" and trailing whitespace from a git describe string, diff --git a/examples/wasm/index.html b/examples/wasm/index.html index 3e8161c..7c9fc61 100644 --- a/examples/wasm/index.html +++ b/examples/wasm/index.html @@ -10,8 +10,11 @@ body { font: 15px/1.6 'SF Mono', Menlo, monospace; background: var(--bg); color: var(--fg); } header { padding: 12px 20px; border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 16px; } header h1 { font-size: 16px; color: var(--accent); } - header .stats { font-size: 12px; opacity: 0.6; margin-left: auto; } + header .stats { font-size: 12px; opacity: 0.6; } header .stats b { color: var(--accent); } + .header-right { margin-left: auto; display: flex; align-items: center; gap: 8px; } + .hdr-btn { font: 13px/1 'SF Mono', Menlo, monospace; color: var(--fg); background: none; border: 1px solid var(--border); border-radius: 4px; padding: 4px 10px; cursor: pointer; text-decoration: none; transition: border-color 0.15s; } + .hdr-btn:hover { border-color: var(--accent); color: var(--accent); } .container { display: grid; grid-template-columns: 1fr 1fr; height: calc(100vh - 48px); } textarea { background: #161b22; color: var(--fg); border: none; border-right: 1px solid var(--border); padding: 16px; font: inherit; resize: none; outline: none; } @@ -51,6 +54,10 @@

    zigmark

    marked.js ms  ·  speedup × +
    + Docs + GitHub +
    diff --git a/flake.nix b/flake.nix index e2b4a6f..bbe028f 100644 --- a/flake.nix +++ b/flake.nix @@ -36,6 +36,7 @@ ./build.zig.zon2json-lock ./src ./include + ./examples ]; }; # Helper: build zigmark at a given optimization level (null = default). @@ -62,6 +63,19 @@ zigmark-safe = withDesc (mkZigmark "ReleaseSafe") "zigmark (ReleaseSafe)"; zigmark-small = withDesc (mkZigmark "ReleaseSmall") "zigmark (ReleaseSmall)"; zigmark-fast = withDesc (mkZigmark "ReleaseFast") "zigmark (ReleaseFast)"; + site = (mkZigmark null).overrideAttrs (_old: { + pname = "zigmark-site"; + buildPhase = "zig build site -Dversion=${version}"; + installPhase = '' + mkdir -p $out + cp -r zig-out/site/. $out/ + tmpdir=$(mktemp -d) + tar -xf $out/docs/sources.tar -C "$tmpdir" --wildcards 'zigmark/*' + tar -cf $out/docs/sources.tar -C "$tmpdir" zigmark + rm -rf "$tmpdir" + ''; + meta.description = "zigmark static site (playground + docs) for zigmark.sc2.in"; + }); } ); From 2b34618cfeb81788436488f86db863350fe62da4 Mon Sep 17 00:00:00 2001 From: TsunamiNoAi Date: Mon, 20 Apr 2026 12:08:31 -0400 Subject: [PATCH 5/6] Integrate Pozeiden for Mermaid.js rendering in WASM output --- build.zig | 12 ++++++++++++ examples/wasm/zigmark-wasm.zig | 17 ++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/build.zig b/build.zig index 7b9d45b..2ec0b23 100644 --- a/build.zig +++ b/build.zig @@ -306,6 +306,17 @@ pub fn build(b: *std.Build) void { zigmark_wasm_mod.addImport("mecha", wasm_mecha.module("mecha")); zigmark_wasm_mod.addImport("dt", wasm_dt.module("datetime")); + const wasm_pozeiden_dep = b.lazyDependency("pozeiden", .{ + .target = wasm_target, + .optimize = wasm_optimize, + }); + const wasm_pozeiden_module = if (wasm_pozeiden_dep) |dep| + dep.module("pozeiden") + else + b.addModule("pozeiden-stub-wasm", .{ + .root_source_file = b.path("src/noop_mermaid.zig"), + }); + const wasm_lib = b.addExecutable(.{ .name = "zigmark", .root_module = b.createModule(.{ @@ -314,6 +325,7 @@ pub fn build(b: *std.Build) void { .optimize = wasm_optimize, .imports = &.{ .{ .name = "zigmark", .module = zigmark_wasm_mod }, + .{ .name = "pozeiden", .module = wasm_pozeiden_module }, }, }), }); diff --git a/examples/wasm/zigmark-wasm.zig b/examples/wasm/zigmark-wasm.zig index fb28bfa..ca52445 100644 --- a/examples/wasm/zigmark-wasm.zig +++ b/examples/wasm/zigmark-wasm.zig @@ -13,6 +13,7 @@ const std = @import("std"); const zigmark = @import("zigmark"); +const pozeiden = @import("pozeiden"); // ── Allocator ──────────────────────────────────────────────────────────────── // Use a fixed-buffer allocator backed by WASM linear memory. @@ -33,12 +34,22 @@ fn freeLastResult() void { // ── Exported API ───────────────────────────────────────────────────────────── -/// Parse Markdown and render to HTML. +/// Parse Markdown and render to HTML, with Mermaid diagrams rendered to SVG. /// `input` is a pointer into WASM linear memory; `len` is the byte length. -/// Returns a pointer to the NUL-terminated result, or 0 on error. +/// Returns a pointer to the result, or 0 on error. /// The pointer is valid until the next call to any render function. export fn render_html(input: [*]const u8, len: usize) usize { - return renderWith(input, len, zigmark.HTMLRenderer); + freeLastResult(); + const slice = input[0..len]; + var parser = zigmark.Parser.init(); + var doc = parser.parseMarkdown(allocator, slice) catch return 0; + defer doc.deinit(allocator); + var aw: std.Io.Writer.Allocating = .init(allocator); + defer aw.deinit(); + zigmark.renderHtmlWithMermaid(allocator, &aw.writer, doc, pozeiden.render) catch return 0; + const buf = aw.toOwnedSlice() catch return 0; + last_result = buf; + return @intFromPtr(buf.ptr); } /// Parse Markdown and render to a human-readable AST tree. From bf3e19d2e81ec2c3594b0c99079e0e9a388ac411 Mon Sep 17 00:00:00 2001 From: TsunamiNoAi Date: Mon, 20 Apr 2026 12:15:28 -0400 Subject: [PATCH 6/6] Update version to 0.6.0 and enhance README with WASM module details and Mermaid.js rendering support --- README.md | 203 ++++++++++++++++++++++++++------------------------ build.zig.zon | 2 +- 2 files changed, 106 insertions(+), 99 deletions(-) diff --git a/README.md b/README.md index 853ee1e..b2e24a4 100644 --- a/README.md +++ b/README.md @@ -95,31 +95,31 @@ Hello **world**. Supported frontmatter fields: -| Field | Type | Default | Description | -| ------------------------------------------------ | -------------- | ------------------- | ----------------------------------------- | -| `title` | string | — | Document title | -| `subtitle` | string | — | Subtitle shown on title page | -| `author` | string or list | — | Author name(s); list uses the first entry | -| `date` | string | — | Date shown on title page | -| `lang` | string | `en` | Document language | -| `paper` | string | `a4` | Paper size (e.g. `a4`, `us-letter`) | -| `fontsize` | string | `11pt` | Base font size | -| `titlepage` | bool | `false` | Generate a full-bleed title page | -| `titlepage-color` | string | `1E3A5F` | Title page background (hex, no `#`) | -| `titlepage-text-color` | string | `FFFFFF` | Title page text colour | -| `titlepage-rule-color` | string | `AAAAAA` | Title page rule colour | -| `titlepage-rule-height` | number | `4` | Title page rule thickness (pt) | -| `toc` | bool | `false` | Insert a table of contents | -| `toc-title` | string | `Contents` | TOC heading | -| `toc-own-page` | bool | `false` | _(reserved, not yet implemented)_ | -| `toc-depth` | number | `3` | TOC depth | -| `numbersections` | bool | `false` | Number headings | -| `disable-header-and-footer` | bool | `false` | Suppress page header and footer | -| `header-left` / `header-center` / `header-right` | string | title / — / date | Header slots | -| `footer-left` / `footer-center` / `footer-right` | string | author / — / page\# | Footer slots | -| `colorlinks` | bool | `true` | Colour hyperlinks | -| `linkcolor` | string | `A50000` | Internal link colour (hex) | -| `urlcolor` | string | `4077C0` | URL link colour (hex) | +| Field | Type | Default | Description | +|---|---|---|---| +| `title` | string | — | Document title | +| `subtitle` | string | — | Subtitle shown on title page | +| `author` | string or list | — | Author name(s); list uses the first entry | +| `date` | string | — | Date shown on title page | +| `lang` | string | `en` | Document language | +| `paper` | string | `a4` | Paper size (e.g. `a4`, `us-letter`) | +| `fontsize` | string | `11pt` | Base font size | +| `titlepage` | bool | `false` | Generate a full-bleed title page | +| `titlepage-color` | string | `1E3A5F` | Title page background (hex, no `#`) | +| `titlepage-text-color` | string | `FFFFFF` | Title page text colour | +| `titlepage-rule-color` | string | `AAAAAA` | Title page rule colour | +| `titlepage-rule-height` | number | `4` | Title page rule thickness (pt) | +| `toc` | bool | `false` | Insert a table of contents | +| `toc-title` | string | `Contents` | TOC heading | +| `toc-own-page` | bool | `false` | _(reserved, not yet implemented)_ | +| `toc-depth` | number | `3` | TOC depth | +| `numbersections` | bool | `false` | Number headings | +| `disable-header-and-footer` | bool | `false` | Suppress page header and footer | +| `header-left` / `header-center` / `header-right` | string | title / — / date | Header slots | +| `footer-left` / `footer-center` / `footer-right` | string | author / — / page\# | Footer slots | +| `colorlinks` | bool | `true` | Colour hyperlinks | +| `linkcolor` | string | `A50000` | Internal link colour (hex) | +| `urlcolor` | string | `4077C0` | URL link colour (hex) | ### AI-Friendly Output @@ -316,12 +316,12 @@ const para_count = query.count(.paragraph); Extract and query structured metadata from the top of a Markdown file. All four formats are normalised to `std.json.Value` for uniform access. -| Format | Opening marker | Example | -| ------ | -------------- | --------------------------- | -| YAML | `---` | `--- \ntitle: Hello\n---` | -| TOML | `+++` | `+++\ntitle = "Hello"\n+++` | -| JSON | `{` | `{"title": "Hello"}` | -| ZON | `.{` | `.{ .title = "Hello" }` | +| Format | Opening marker | Example | +|---|---|---| +| YAML | `---` | `--- \ntitle: Hello\n---` | +| TOML | `+++` | `+++\ntitle = "Hello"\n+++` | +| JSON | `{` | `{"title": "Hello"}` | +| ZON | `.{` | `.{ .title = "Hello" }` | ```zig const FrontMatter = zigmark.FrontMatter; @@ -416,10 +416,10 @@ Library.sortBy(results, "title", true); Tokens are whitespace-separated and may appear in any order. -| Token | Meaning | -| ------------- | -------------------------------------------------- | -| `path` | frontmatter field at `path` must exist | -| `path=value` | frontmatter field at `path` must equal `value` | +| Token | Meaning | +|---|---| +| `path` | frontmatter field at `path` must exist | +| `path=value` | frontmatter field at `path` must equal `value` | | `@block_type` | select blocks of this type from matching documents | Multiple `path` / `path=value` tokens are **AND-combined**: a document must satisfy every filter to appear in results. @@ -452,11 +452,11 @@ try lib.query(allocator, "taxonomies.SCF @fenced_code_block") #### Result fields -| Field | Type | Description | -| ------------ | ---------------------- | ---------------------------------------------------------------- | -| `entry` | `*const Library.Entry` | The matching document (`.document`, `.frontmatter`, `.path`) | -| `block` | `?*const AST.Block` | The specific block that matched, or `null` for doc-level results | -| `confidence` | `f32` | Match confidence in `[0.0, 1.0]`; results sorted descending | +| Field | Type | Description | +|---|---|---| +| `entry` | `*const Library.Entry` | The matching document (`.document`, `.frontmatter`, `.path`) | +| `block` | `?*const AST.Block` | The specific block that matched, or `null` for doc-level results | +| `confidence` | `f32` | Match confidence in `[0.0, 1.0]`; results sorted descending | Documents without frontmatter are supported — they simply never match frontmatter filters. @@ -576,45 +576,45 @@ LD_LIBRARY_PATH=zig-out/lib ./example Every section of the [CommonMark 0.31.2](https://spec.commonmark.org/0.31.2/) spec passes: -| Section | Tests | -| --------------------------------------- | ----- | -| Tabs | 11 | -| Backslash escapes | 13 | -| Entity and numeric character references | 17 | -| Precedence | 1 | -| Thematic breaks | 19 | -| ATX headings | 18 | -| Setext headings | 27 | -| Indented code blocks | 12 | -| Fenced code blocks | 29 | -| HTML blocks | 44 | -| Link reference definitions | 27 | -| Paragraphs | 8 | -| Blank lines | 1 | -| Block quotes | 25 | -| List items | 48 | -| Lists | 27 | -| Code spans | 22 | -| Emphasis and strong emphasis | 132 | -| Links | 90 | -| Images | 22 | -| Autolinks | 19 | -| Raw HTML | 20 | -| Hard line breaks | 15 | -| Soft line breaks | 2 | -| Textual content | 3 | +| Section | Tests | +|---|---| +| Tabs | 11 | +| Backslash escapes | 13 | +| Entity and numeric character references | 17 | +| Precedence | 1 | +| Thematic breaks | 19 | +| ATX headings | 18 | +| Setext headings | 27 | +| Indented code blocks | 12 | +| Fenced code blocks | 29 | +| HTML blocks | 44 | +| Link reference definitions | 27 | +| Paragraphs | 8 | +| Blank lines | 1 | +| Block quotes | 25 | +| List items | 48 | +| Lists | 27 | +| Code spans | 22 | +| Emphasis and strong emphasis | 132 | +| Links | 90 | +| Images | 22 | +| Autolinks | 19 | +| Raw HTML | 20 | +| Hard line breaks | 15 | +| Soft line breaks | 2 | +| Textual content | 3 | ### GFM Extensions — 24/24 ✅ All [GitHub Flavored Markdown](https://github.github.com/gfm/) extensions pass. -| GFM Extension | Tests | -| -------------------- | -------- | -| Tables | 8/8 ✅ | -| Task list items | 2/2 ✅ | -| Strikethrough | 2/2 ✅ | +| GFM Extension | Tests | +|---|---| +| Tables | 8/8 ✅ | +| Task list items | 2/2 ✅ | +| Strikethrough | 2/2 ✅ | | Autolinks (extended) | 11/11 ✅ | -| Disallowed raw HTML | 1/1 ✅ | +| Disallowed raw HTML | 1/1 ✅ | **Tables** — pipe-delimited with column alignment (`---`, `:---`, `---:`, `:---:`): @@ -694,9 +694,13 @@ zig-out/ ├── lib/libzigmark.so # C-callable shared library ├── include/zigmark.h # C header ├── docs/ # Generated documentation -└── wasm/ # WebAssembly module (zig build wasm) +├── wasm/ # WebAssembly module (zig build wasm) +│ ├── zigmark.wasm +│ └── index.html # Live preview demo +└── site/ # Combined site (zig build site) ├── zigmark.wasm - └── index.html # Live preview demo + ├── index.html # Playground + └── docs/ # API docs ``` ### WASM @@ -737,6 +741,9 @@ nix develop # WASM live preview demo nix run .#wasm-demo +# Build combined site (playground + docs) for zigmark.sc2.in +nix build .#site + # Run CLI performance benchmark (compares zigmark vs cmark, updates README) nix run .#bench ``` @@ -747,8 +754,8 @@ Requires **Zig 0.15.2** or later. - **`Parser`** — Block-level + inline two-pass parser built on the [mecha](https://github.com/Hejsil/mecha) parser combinator library; accepts a `[]const u8` via `parseMarkdown` or any `*std.Io.Reader` via `parseFromReader` - **`AST`** — Typed union-based Abstract Syntax Tree (`Document` → `Block` → `Inline`) -- **`HTMLRenderer`** — CommonMark-compliant HTML serialiser -- **`TypstRenderer`** — Typst markup renderer; `typst.renderDocument` adds an eisvogel-inspired preamble (title page, TOC, headers/footers, styled code blocks and blockquotes) driven by `DocumentOptions` +- **`HTMLRenderer`** — CommonMark-compliant HTML serialiser; `renderHtmlWithMermaid` accepts an optional `pozeiden.render`-compatible function to convert `mermaid`/`mermaidjs` fenced blocks to inline SVG `
    ` elements (falls back to a plain code block on error or when `null`) +- **`TypstRenderer`** — Typst markup renderer; `typst.renderDocument` adds an eisvogel-inspired preamble (title page, TOC, headers/footers, styled code blocks and blockquotes) driven by `DocumentOptions`; `renderTypstDocWithMermaid` converts `mermaid`/`mermaidjs` blocks to `#image.decode` calls - **`ASTRenderer`** — Human-readable tree diagram with box-drawing characters - **`AIRenderer`** — Token-efficient AST representation for LLM consumption - **`MarkdownRenderer`** — AST→Markdown normaliser; converts headings to ATX, links to inline, code blocks to fenced @@ -761,33 +768,33 @@ Requires **Zig 0.15.2** or later. -_Last updated: 2026-03-21 · input: `README.md` (26 KB) · run `nix run .#bench` to reproduce_ +_Last updated: 2026-04-20 · input: `README.md` (31 KB) · run `nix run .#bench` to reproduce_ ### Speed -| Command | Mean \[ms\] | Min \[ms\] | Max \[ms\] | Relative | -| :--------------------------- | -----------: | ---------: | ---------: | ----------: | -| `lowdown` | 3.5 ± 0.5 | 2.4 | 5.3 | 1.00 | -| `discount` | 3.7 ± 0.6 | 2.6 | 6.2 | 1.08 ± 0.25 | -| **`zigmark (ReleaseFast)`** | 4.8 ± 0.7 | 3.4 | 8.0 | 1.39 ± 0.29 | -| **`zigmark (ReleaseSafe)`** | 5.1 ± 0.8 | 3.7 | 12.9 | 1.47 ± 0.32 | -| **`zigmark (ReleaseSmall)`** | 5.3 ± 0.7 | 3.8 | 7.6 | 1.54 ± 0.32 | -| `cmark` | 7.0 ± 1.1 | 5.1 | 10.3 | 2.02 ± 0.45 | -| `cmark-gfm` | 8.1 ± 1.2 | 5.9 | 20.0 | 2.35 ± 0.51 | -| `pandoc` | 197.8 ± 15.3 | 176.9 | 234.2 | 1.00 | +| Command | Mean \[ms\] | Min \[ms\] | Max \[ms\] | Relative | +|:---|---:|---:|---:|---:| +| `lowdown` | 2.6 ± 1.4 | 1.3 | 13.2 | 1.00 | +| **`zigmark (ReleaseFast)`** | 3.5 ± 2.1 | 1.9 | 24.3 | 1.38 ± 1.12 | +| `discount` | 3.5 ± 1.8 | 1.7 | 18.5 | 1.37 ± 1.04 | +| **`zigmark (ReleaseSafe)`** | 4.0 ± 2.3 | 2.0 | 20.3 | 1.55 ± 1.24 | +| **`zigmark (ReleaseSmall)`** | 4.0 ± 1.6 | 2.3 | 18.6 | 1.57 ± 1.08 | +| `cmark-gfm` | 5.6 ± 1.9 | 3.2 | 17.4 | 2.20 ± 1.45 | +| `cmark` | 5.9 ± 2.3 | 3.1 | 23.7 | 2.31 ± 1.59 | +| `pandoc` | 211.4 ± 34.5 | 167.7 | 278.4 | 1.00 | ### Memory (peak RSS) -| Command | Peak RSS (KB) | -| :--------------------------- | ------------: | -| **`zigmark (ReleaseSmall)`** | 1540 | -| **`zigmark (ReleaseFast)`** | 2000 | -| `discount` | 2072 | -| **`zigmark (ReleaseSafe)`** | 2084 | -| `lowdown` | 3112 | -| `cmark-gfm` | 4256 | -| `cmark` | 4264 | -| `pandoc` | 132208 | +| Command | Peak RSS (KB) | +|:---|---:| +| **`zigmark (ReleaseSmall)`** | 1856 | +| **`zigmark (ReleaseFast)`** | 2116 | +| `discount` | 2168 | +| **`zigmark (ReleaseSafe)`** | 2372 | +| `lowdown` | 3040 | +| `cmark` | 4104 | +| `cmark-gfm` | 4168 | +| `pandoc` | 128628 | diff --git a/build.zig.zon b/build.zig.zon index 7142c05..ea96fb3 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -9,7 +9,7 @@ .name = .zigmark, // This is a [Semantic Version](https://semver.org/). // In a future version of Zig it will be used for package deduplication. - .version = "0.5.0", + .version = "0.6.0", // Together with name, this represents a globally unique package // identifier. This field is generated by the Zig toolchain when the // package is first created, and then *never changes*. This allows