Add Render AST and text exporters: JSON, Markdown, HTML simple/rich
- Render AST: placeholder expansion, hierarchical numbering (1, 1.1), hidden/skipped filtering, preview step limit, annotated image rendering - JSON exporter with sidecar annotated PNGs - Markdown exporter: TOC with resolving anchors, text blocks as blockquotes, fenced code, tables, Azure-wiki image sizing option - Self-contained HTML exporters (data-URI images, zero external refs); rich variant adds floating TOC, checkboxes, localStorage progress - HTML->Markdown converter for the sanitizer-allowed tag set - 7 exporter workflow tests (42 total) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { guideSlug, writeStepImages } = require('./common');
|
||||
|
||||
/**
|
||||
* JSON exporter: structured guide + steps, annotated screenshots written to
|
||||
* a sidecar steps-<title>/ folder, image paths relative to the JSON file.
|
||||
*/
|
||||
|
||||
const DEFAULT_TEMPLATE = {
|
||||
pretty: true,
|
||||
includeImages: true,
|
||||
includeAnnotations: true,
|
||||
};
|
||||
|
||||
function exportJson(ast, outDir, template = {}) {
|
||||
const tpl = { ...DEFAULT_TEMPLATE, ...template };
|
||||
fs.mkdirSync(outDir, { recursive: true });
|
||||
const images = tpl.includeImages ? writeStepImages(ast, outDir) : new Map();
|
||||
|
||||
const doc = {
|
||||
format: 'stepforge-guide',
|
||||
version: 1,
|
||||
generatedAt: ast.generatedAt,
|
||||
guide: {
|
||||
title: ast.guide.title,
|
||||
descriptionHtml: ast.guide.descriptionHtml,
|
||||
createdAt: ast.guide.createdAt,
|
||||
updatedAt: ast.guide.updatedAt,
|
||||
},
|
||||
steps: ast.steps.map((step) => ({
|
||||
number: step.number,
|
||||
kind: step.kind,
|
||||
status: step.status,
|
||||
title: step.title,
|
||||
descriptionHtml: step.descriptionHtml,
|
||||
descriptionText: step.descriptionText,
|
||||
image: images.has(step.stepId) ? images.get(step.stepId) : null,
|
||||
annotations: tpl.includeAnnotations ? step.annotations : undefined,
|
||||
textBlocks: step.textBlocks.map((tb) => ({
|
||||
position: tb.position, level: tb.level, title: tb.title, descriptionHtml: tb.descriptionHtml,
|
||||
})),
|
||||
codeBlocks: step.codeBlocks,
|
||||
tableBlocks: step.tableBlocks,
|
||||
links: step.links,
|
||||
})),
|
||||
};
|
||||
|
||||
const file = path.join(outDir, `${guideSlug(ast)}.json`);
|
||||
fs.writeFileSync(file, JSON.stringify(doc, null, tpl.pretty ? 2 : 0) + '\n');
|
||||
return { file, imageCount: images.size };
|
||||
}
|
||||
|
||||
module.exports = { exportJson, DEFAULT_TEMPLATE };
|
||||
Reference in New Issue
Block a user