feat(f7.1): render augmented mermaid diff in changed-block branch (#22)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-06-11 16:33:12 -07:00
parent e938572ebc
commit 6702490497
2 changed files with 67 additions and 5 deletions
+32 -2
View File
@@ -10,6 +10,7 @@
*/
import MarkdownIt from "markdown-it";
import { diffArrays, diffWords } from "diff";
import { diffMermaid } from "./mermaidDiff";
export type BlockType = "prose" | "code" | "mermaid";
@@ -204,6 +205,24 @@ md.renderer.rules.fence = (tokens, idx, options, env, self) => {
return defaultFence(tokens, idx, options, env, self);
};
/** Inner source of a ```mermaid fence (drops the fence lines), or null. */
function mermaidFenceBody(raw: string): string | null {
const lines = raw.split(/\r?\n/);
const open = lines[0]?.match(/^(\s*)(`{3,}|~{3,})\s*(\w+)?/);
if (!open || open[3]?.toLowerCase() !== "mermaid") return null;
const body = lines.slice(1);
if (body.length && /^(\s*)(`{3,}|~{3,})/.test(body[body.length - 1])) body.pop();
return body.join("\n");
}
/** F7.1 (#22): legend shown beneath an intra-diagram-diffed mermaid block. */
const MERMAID_LEGEND =
'<div class="cw-mermaid-legend">' +
'<span class="cw-leg cw-leg-add">added</span>' +
'<span class="cw-leg cw-leg-chg">changed</span>' +
'<span class="cw-leg cw-leg-rem">removed</span>' +
"</div>";
/** Build a markdown string with inline <ins>/<del> from a word-level prose diff. */
function wordMergedMarkdown(beforeRaw: string, afterRaw: string): string {
return diffWords(beforeRaw, afterRaw)
@@ -256,8 +275,19 @@ function renderOp(op: BlockOp, render: (src: string) => string): string {
case "changed":
cls = "cw-changed";
if (op.atomic) {
inner = safe(op.block.raw); // the NEW block, whole (INV-23)
badge = '<span class="cw-badge">changed</span>';
const curBody = op.block.type === "mermaid" ? mermaidFenceBody(op.block.raw) : null;
const beforeBody = op.before.type === "mermaid" ? mermaidFenceBody(op.before.raw) : null;
const md =
curBody !== null && beforeBody !== null
? diffMermaid(beforeBody, curBody)
: { kind: "fallback" as const };
if (md.kind === "augmented") {
// Re-render the augmented diagram as a mermaid fence; add a legend, drop the badge (INV-29).
inner = safe("```mermaid\n" + md.source + "\n```") + MERMAID_LEGEND;
} else {
inner = safe(op.block.raw); // the NEW block, whole (INV-23 / INV-30 fallback)
badge = '<span class="cw-badge">changed</span>';
}
} else {
inner = safe(wordMergedMarkdown(op.before.raw, op.block.raw));
}