diff --git a/exporters/markdown-guide.js b/exporters/markdown-guide.js
index a10fcfc..297641e 100644
--- a/exporters/markdown-guide.js
+++ b/exporters/markdown-guide.js
@@ -2,6 +2,7 @@
const fs = require('node:fs');
const path = require('node:path');
+const { escapeHtml } = require('../core/util');
const { guideSlug, writeStepImages, LEVEL_LABEL, stepContentGroups, codeBlockText } = require('./common');
const { htmlToMarkdown } = require('./htmlmd');
const { tocEntries, guideMetaLines, guideSummary } = require('./document-layout');
@@ -20,6 +21,13 @@ const WIKIJS_CALLOUT_CLASS = {
error: 'is-danger',
};
+const HTML_CALLOUT_THEME = {
+ info: { label: 'Note', border: '#2563eb', bg: '#eff6ff', fg: '#1d4ed8', kind: 'note' },
+ success: { label: 'Tip', border: '#10b981', bg: '#ecfdf5', fg: '#047857', kind: 'tip' },
+ warn: { label: 'Warning', border: '#f59e0b', bg: '#fffbeb', fg: '#b45309', kind: 'warning' },
+ error: { label: 'Important', border: '#ef4444', bg: '#fef2f2', fg: '#b91c1c', kind: 'important' },
+};
+
function anchorFor(step) {
return `step-${step.number.replace(/\./g, '-')}`;
}
@@ -30,6 +38,18 @@ function quoteBody(text) {
function emitBlock(lines, tb, { alertStyle = 'gfm' } = {}) {
const body = htmlToMarkdown(tb.descriptionHtml);
+ if (alertStyle === 'html') {
+ const theme = HTML_CALLOUT_THEME[tb.level] || HTML_CALLOUT_THEME.info;
+ const label = theme.label;
+ const title = tb.title ? `${label}: ${tb.title}` : label;
+ lines.push(
+ `
`,
+ `
${escapeHtml(title)}
`,
+ );
+ if (body) lines.push(`
${tb.descriptionHtml || ''}
`);
+ lines.push('
', '');
+ return;
+ }
if (alertStyle === 'wikijs') {
const label = tb.title || LEVEL_LABEL[tb.level] || 'Note';
const className = WIKIJS_CALLOUT_CLASS[tb.level] || 'is-info';
diff --git a/exporters/markdown.js b/exporters/markdown.js
index 8eb9aa7..dc71773 100644
--- a/exporters/markdown.js
+++ b/exporters/markdown.js
@@ -10,7 +10,7 @@ const { DEFAULT_TEMPLATE, anchorFor, renderMarkdownGuide } = require('./markdown
function exportMarkdown(ast, outDir, template = {}) {
return renderMarkdownGuide(ast, outDir, template, {
defaults: DEFAULT_TEMPLATE,
- alertStyle: 'gfm',
+ alertStyle: 'html',
tocTitle: 'Contents',
fileExt: '.md',
});
diff --git a/tests/unit/exporters-text.test.js b/tests/unit/exporters-text.test.js
index e919ce5..2a6b978 100644
--- a/tests/unit/exporters-text.test.js
+++ b/tests/unit/exporters-text.test.js
@@ -137,11 +137,11 @@ test('Markdown export: TOC anchors resolve, images exist, blocks rendered', (t)
assert.equal(lines[fenceStart + 1], '0 2 * * * /usr/local/bin/acmesync --backup');
assert.equal(lines[fenceStart + 2], '```');
assert.ok(lines.some((l) => /^\| Day \| Window \|$/.test(l)), 'table header row');
- // Warning text block became a GFM alert blockquote with its content.
- const warnIdx = lines.findIndex((l) => l.startsWith('> [!WARNING]'));
- assert.ok(warnIdx > 0);
- assert.equal(lines[warnIdx + 1], '> **Access**');
- assert.equal(lines[warnIdx + 2], '> Admins only.');
+ // Warning text block became a styled HTML callout with its content.
+ assert.ok(md.includes('Admins only.'));
});
test('Wiki.js export: TOC is included, wiki callouts render, images exist', (t) => {