Fix export block ordering, stale per-step UI, and cross-step block loss
Exporters now interleave text/code/table blocks in the same order they appear in the editor's Blocks panel (via a shared stepContentGroups helper) instead of grouping by kind, so exported docs match the guide editor's ordering. selectStep() now also refreshes the Focused View controls and Blocks panel (previously only done by renderAll), so switching steps no longer leaves the previous step's blocks/focused-view sliders on screen. It also flushes any pending edits on the outgoing step before switching, so a later guide-wide reload (e.g. applying an annotation style to the whole guide) can't discard unsaved text-block edits on other steps.
This commit is contained in:
@@ -1281,13 +1281,19 @@ class GuideEditor {
|
|||||||
|
|
||||||
async selectStep(stepId) {
|
async selectStep(stepId) {
|
||||||
if (!this.stepMap.has(stepId)) return;
|
if (!this.stepMap.has(stepId)) return;
|
||||||
|
// Persist any unsaved edits on the outgoing step before switching, so a
|
||||||
|
// later guide-wide reload (e.g. applyStyleAcross('guide')) doesn't
|
||||||
|
// discard them by re-fetching a stale on-disk copy.
|
||||||
|
if (this.pendingSave) await this.flushStep();
|
||||||
this.selectedStepId = stepId;
|
this.selectedStepId = stepId;
|
||||||
this.selectedAnnotationId = null;
|
this.selectedAnnotationId = null;
|
||||||
this.canvas.select(null);
|
this.canvas.select(null);
|
||||||
this.syncStepFields();
|
this.syncStepFields();
|
||||||
|
this.syncFocusedControls();
|
||||||
this.renderStepList();
|
this.renderStepList();
|
||||||
this.renderCanvas();
|
this.renderCanvas();
|
||||||
this.renderAnnotationPanel();
|
this.renderAnnotationPanel();
|
||||||
|
this.renderBlocksPanel();
|
||||||
this.emitMeta();
|
this.emitMeta();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,20 @@ function stepBlocks(step) {
|
|||||||
return step.blocks || orderedBlocks(step);
|
return step.blocks || orderedBlocks(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Split a step's blocks into the groups exporters lay out around the
|
||||||
|
* description/image: text blocks pinned to 'before-description', and
|
||||||
|
* everything else (code/table blocks plus 'after-description' and
|
||||||
|
* 'after-image' text blocks) in the same relative order they appear in the
|
||||||
|
* editor's Blocks list.
|
||||||
|
*/
|
||||||
|
function stepContentGroups(step) {
|
||||||
|
const all = stepBlocks(step);
|
||||||
|
const before = all.filter((b) => b.kind === 'text' && b.position === 'before-description');
|
||||||
|
const rest = all.filter((b) => !(b.kind === 'text' && b.position === 'before-description'));
|
||||||
|
return { before, rest };
|
||||||
|
}
|
||||||
|
|
||||||
function codeBlockText(block) {
|
function codeBlockText(block) {
|
||||||
return blockText(block);
|
return blockText(block);
|
||||||
}
|
}
|
||||||
@@ -67,6 +81,7 @@ module.exports = {
|
|||||||
writeStepImages,
|
writeStepImages,
|
||||||
renderAllImages,
|
renderAllImages,
|
||||||
stepBlocks,
|
stepBlocks,
|
||||||
|
stepContentGroups,
|
||||||
codeBlockText,
|
codeBlockText,
|
||||||
LEVEL_LABEL,
|
LEVEL_LABEL,
|
||||||
};
|
};
|
||||||
|
|||||||
+7
-11
@@ -4,7 +4,7 @@ const fs = require('node:fs');
|
|||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const { slugify, escapeXml } = require('../core/util');
|
const { slugify, escapeXml } = require('../core/util');
|
||||||
const { encodePng } = require('../core/png');
|
const { encodePng } = require('../core/png');
|
||||||
const { guideSlug, renderAllImages, stepBlocks, codeBlockText } = require('./common');
|
const { guideSlug, renderAllImages, stepContentGroups, codeBlockText } = require('./common');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Confluence storage-format export. Writes a single XHTML document plus a
|
* Confluence storage-format export. Writes a single XHTML document plus a
|
||||||
@@ -67,7 +67,8 @@ function exportConfluence(ast, outDir, template = {}) {
|
|||||||
const parts = [`<a id="${anchorFor(step)}"></a>`, `<h2>${escapeXml(step.number)}. ${escapeXml(step.title || 'Untitled step')}</h2>`];
|
const parts = [`<a id="${anchorFor(step)}"></a>`, `<h2>${escapeXml(step.number)}. ${escapeXml(step.title || 'Untitled step')}</h2>`];
|
||||||
if (step.skipped) parts.push('<p><em>(skipped)</em></p>');
|
if (step.skipped) parts.push('<p><em>(skipped)</em></p>');
|
||||||
|
|
||||||
for (const tb of stepBlocks(step).filter((block) => block.kind === 'text' && block.position === 'before-description')) {
|
const { before, rest } = stepContentGroups(step);
|
||||||
|
for (const tb of before) {
|
||||||
parts.push(blockMacro(tb, ast));
|
parts.push(blockMacro(tb, ast));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,8 +81,10 @@ function exportConfluence(ast, outDir, template = {}) {
|
|||||||
parts.push(`<p><ac:image><ri:attachment ri:filename="${escapeXml(attachment)}" /></ac:image></p>`);
|
parts.push(`<p><ac:image><ri:attachment ri:filename="${escapeXml(attachment)}" /></ac:image></p>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const block of stepBlocks(step).filter((item) => item.kind !== 'text')) {
|
for (const block of rest) {
|
||||||
if (block.kind === 'code') {
|
if (block.kind === 'text') {
|
||||||
|
parts.push(blockMacro(block, ast));
|
||||||
|
} else if (block.kind === 'code') {
|
||||||
const lang = block.language ? `<ac:parameter ac:name="language">${escapeXml(block.language)}</ac:parameter>` : '';
|
const lang = block.language ? `<ac:parameter ac:name="language">${escapeXml(block.language)}</ac:parameter>` : '';
|
||||||
parts.push(`<ac:structured-macro ac:name="code">${lang}<ac:plain-text-body>${cdata(codeBlockText(block))}</ac:plain-text-body></ac:structured-macro>`);
|
parts.push(`<ac:structured-macro ac:name="code">${lang}<ac:plain-text-body>${cdata(codeBlockText(block))}</ac:plain-text-body></ac:structured-macro>`);
|
||||||
} else if (block.kind === 'table') {
|
} else if (block.kind === 'table') {
|
||||||
@@ -97,13 +100,6 @@ function exportConfluence(ast, outDir, template = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const tb of stepBlocks(step).filter((block) => block.kind === 'text' && block.position === 'after-description')) {
|
|
||||||
parts.push(blockMacro(tb, ast));
|
|
||||||
}
|
|
||||||
for (const tb of stepBlocks(step).filter((block) => block.kind === 'text' && block.position === 'after-image')) {
|
|
||||||
parts.push(blockMacro(tb, ast));
|
|
||||||
}
|
|
||||||
|
|
||||||
return `<div class="step">${parts.join('\n')}</div>`;
|
return `<div class="step">${parts.join('\n')}</div>`;
|
||||||
}).join('\n');
|
}).join('\n');
|
||||||
|
|
||||||
|
|||||||
+8
-9
@@ -5,7 +5,7 @@ const path = require('node:path');
|
|||||||
const { zipSync } = require('../core/zip');
|
const { zipSync } = require('../core/zip');
|
||||||
const { escapeXml } = require('../core/util');
|
const { escapeXml } = require('../core/util');
|
||||||
const { encodePng } = require('../core/png');
|
const { encodePng } = require('../core/png');
|
||||||
const { guideSlug, renderAllImages, LEVEL_LABEL, stepBlocks, codeBlockText } = require('./common');
|
const { guideSlug, renderAllImages, LEVEL_LABEL, stepContentGroups, codeBlockText } = require('./common');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DOCX exporter: WordprocessingML built directly (no dependency), one
|
* DOCX exporter: WordprocessingML built directly (no dependency), one
|
||||||
@@ -99,7 +99,8 @@ function exportDocx(ast, outDir, template = {}) {
|
|||||||
body.push(p(run(`${step.number}. ${step.title || 'Untitled step'}`, { bold: true, size: headSize }),
|
body.push(p(run(`${step.number}. ${step.title || 'Untitled step'}`, { bold: true, size: headSize }),
|
||||||
step.forceNewPage ? '<w:pageBreakBefore/>' : ''));
|
step.forceNewPage ? '<w:pageBreakBefore/>' : ''));
|
||||||
|
|
||||||
emitTextBlocks(step, 'before-description');
|
const { before, rest } = stepContentGroups(step);
|
||||||
|
for (const tb of before) emitTextBlock(tb);
|
||||||
if (step.descriptionText) body.push(p(run(step.descriptionText)));
|
if (step.descriptionText) body.push(p(run(step.descriptionText)));
|
||||||
|
|
||||||
const img = images.get(step.stepId);
|
const img = images.get(step.stepId);
|
||||||
@@ -111,20 +112,19 @@ function exportDocx(ast, outDir, template = {}) {
|
|||||||
body.push(p(drawing(relCounter, img.width, img.height, tpl.imageWidthTwips)));
|
body.push(p(drawing(relCounter, img.width, img.height, tpl.imageWidthTwips)));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const block of stepBlocks(step).filter((item) => item.kind !== 'text')) {
|
for (const block of rest) {
|
||||||
if (block.kind === 'code') {
|
if (block.kind === 'text') {
|
||||||
|
emitTextBlock(block);
|
||||||
|
} else if (block.kind === 'code') {
|
||||||
body.push(p(run(codeBlockText(block), { size: 18, font: 'Courier New', color: '1F2937' }),
|
body.push(p(run(codeBlockText(block), { size: 18, font: 'Courier New', color: '1F2937' }),
|
||||||
'<w:shd w:val="clear" w:fill="F3F4F6"/>'));
|
'<w:shd w:val="clear" w:fill="F3F4F6"/>'));
|
||||||
} else if (block.kind === 'table') {
|
} else if (block.kind === 'table') {
|
||||||
if (block.rows && block.rows.length) body.push(table(block.rows), p(''));
|
if (block.rows && block.rows.length) body.push(table(block.rows), p(''));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emitTextBlocks(step, 'after-description');
|
|
||||||
emitTextBlocks(step, 'after-image');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitTextBlocks(step, position) {
|
function emitTextBlock(tb) {
|
||||||
for (const tb of stepBlocks(step).filter((b) => b.kind === 'text' && b.position === position)) {
|
|
||||||
const label = `${LEVEL_LABEL[tb.level] || 'Note'}${tb.title ? `: ${tb.title}` : ''}`;
|
const label = `${LEVEL_LABEL[tb.level] || 'Note'}${tb.title ? `: ${tb.title}` : ''}`;
|
||||||
const style = LEVEL_STYLE[tb.level] || LEVEL_STYLE.info;
|
const style = LEVEL_STYLE[tb.level] || LEVEL_STYLE.info;
|
||||||
body.push(p(
|
body.push(p(
|
||||||
@@ -132,7 +132,6 @@ function exportDocx(ast, outDir, template = {}) {
|
|||||||
`<w:shd w:val="clear" w:fill="${style.fill}"/><w:pBdr><w:left w:val="single" w:sz="24" w:space="4" w:color="${style.color}"/></w:pBdr>`
|
`<w:shd w:val="clear" w:fill="${style.fill}"/><w:pBdr><w:left w:val="single" w:sz="24" w:space="4" w:color="${style.color}"/></w:pBdr>`
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const documentXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
const documentXml = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
||||||
|
|||||||
+11
-13
@@ -4,7 +4,7 @@ const fs = require('node:fs');
|
|||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const { escapeHtml } = require('../core/util');
|
const { escapeHtml } = require('../core/util');
|
||||||
const { encodePng } = require('../core/png');
|
const { encodePng } = require('../core/png');
|
||||||
const { guideSlug, renderAllImages, LEVEL_LABEL, stepBlocks, codeBlockText } = require('./common');
|
const { guideSlug, renderAllImages, LEVEL_LABEL, stepContentGroups, codeBlockText } = require('./common');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTML exporters. Both variants are fully self-contained single files:
|
* HTML exporters. Both variants are fully self-contained single files:
|
||||||
@@ -38,34 +38,32 @@ function stepLinkRewrite(html, ast) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function blocksHtml(step, position) {
|
function blockHtml(tb) {
|
||||||
return stepBlocks(step)
|
return `<div class="block block-${tb.level}"><strong>${escapeHtml(LEVEL_LABEL[tb.level] || 'Note')}${tb.title ? `: ${escapeHtml(tb.title)}` : ''}</strong>${tb.descriptionHtml ? `<div>${tb.descriptionHtml}</div>` : ''}</div>`;
|
||||||
.filter((tb) => tb.position === position)
|
|
||||||
.map((tb) => `<div class="block block-${tb.level}"><strong>${escapeHtml(LEVEL_LABEL[tb.level] || 'Note')}${tb.title ? `: ${escapeHtml(tb.title)}` : ''}</strong>${tb.descriptionHtml ? `<div>${tb.descriptionHtml}</div>` : ''}</div>`)
|
|
||||||
.join('\n');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function stepBodyHtml(step, ast, images, tpl) {
|
function stepBodyHtml(step, ast, images, tpl) {
|
||||||
const parts = [];
|
const parts = [];
|
||||||
parts.push(blocksHtml(step, 'before-description'));
|
const { before, rest } = stepContentGroups(step);
|
||||||
|
for (const tb of before) parts.push(blockHtml(tb));
|
||||||
if (step.descriptionHtml) parts.push(`<div class="desc">${stepLinkRewrite(step.descriptionHtml, ast)}</div>`);
|
if (step.descriptionHtml) parts.push(`<div class="desc">${stepLinkRewrite(step.descriptionHtml, ast)}</div>`);
|
||||||
const img = images.get(step.stepId);
|
const img = images.get(step.stepId);
|
||||||
if (img && tpl.includeImages) {
|
if (img && tpl.includeImages) {
|
||||||
parts.push(`<img class="shot" alt="Step ${escapeHtml(step.number)}" src="${dataUri(img)}" width="${img.width}">`);
|
parts.push(`<img class="shot" alt="Step ${escapeHtml(step.number)}" src="${dataUri(img)}" width="${img.width}">`);
|
||||||
}
|
}
|
||||||
for (const block of stepBlocks(step).filter((item) => item.kind !== 'text')) {
|
for (const block of rest) {
|
||||||
if (block.kind === 'code') {
|
if (block.kind === 'text') {
|
||||||
|
parts.push(blockHtml(block));
|
||||||
|
} else if (block.kind === 'code') {
|
||||||
parts.push(`<pre class="code"><code>${escapeHtml(codeBlockText(block))}</code></pre>`);
|
parts.push(`<pre class="code"><code>${escapeHtml(codeBlockText(block))}</code></pre>`);
|
||||||
} else if (block.kind === 'table') {
|
} else if (block.kind === 'table') {
|
||||||
if (!block.rows || !block.rows.length) continue;
|
if (!block.rows || !block.rows.length) continue;
|
||||||
const [head, ...rest] = block.rows;
|
const [head, ...bodyRows] = block.rows;
|
||||||
parts.push('<table><thead><tr>' + head.map((c) => `<th>${escapeHtml(c)}</th>`).join('') + '</tr></thead><tbody>'
|
parts.push('<table><thead><tr>' + head.map((c) => `<th>${escapeHtml(c)}</th>`).join('') + '</tr></thead><tbody>'
|
||||||
+ rest.map((r) => '<tr>' + r.map((c) => `<td>${escapeHtml(c)}</td>`).join('') + '</tr>').join('')
|
+ bodyRows.map((r) => '<tr>' + r.map((c) => `<td>${escapeHtml(c)}</td>`).join('') + '</tr>').join('')
|
||||||
+ '</tbody></table>');
|
+ '</tbody></table>');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parts.push(blocksHtml(step, 'after-description'));
|
|
||||||
parts.push(blocksHtml(step, 'after-image'));
|
|
||||||
return parts.filter(Boolean).join('\n');
|
return parts.filter(Boolean).join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-10
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const fs = require('node:fs');
|
const fs = require('node:fs');
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const { guideSlug, writeStepImages, LEVEL_LABEL, stepBlocks, codeBlockText } = require('./common');
|
const { guideSlug, writeStepImages, LEVEL_LABEL, stepContentGroups, codeBlockText } = require('./common');
|
||||||
const { htmlToMarkdown } = require('./htmlmd');
|
const { htmlToMarkdown } = require('./htmlmd');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,7 +45,8 @@ function exportMarkdown(ast, outDir, template = {}) {
|
|||||||
lines.push(`${heading} ${step.number}. ${step.title || 'Untitled step'}`, '');
|
lines.push(`${heading} ${step.number}. ${step.title || 'Untitled step'}`, '');
|
||||||
if (step.skipped) lines.push('*(skipped)*', '');
|
if (step.skipped) lines.push('*(skipped)*', '');
|
||||||
|
|
||||||
emitBlocks(lines, step, 'before-description');
|
const { before, rest } = stepContentGroups(step);
|
||||||
|
for (const tb of before) emitBlock(lines, tb);
|
||||||
|
|
||||||
if (step.descriptionHtml) lines.push(htmlToMarkdown(step.descriptionHtml), '');
|
if (step.descriptionHtml) lines.push(htmlToMarkdown(step.descriptionHtml), '');
|
||||||
|
|
||||||
@@ -58,8 +59,10 @@ function exportMarkdown(ast, outDir, template = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const block of stepBlocks(step).filter((item) => item.kind !== 'text')) {
|
for (const block of rest) {
|
||||||
if (block.kind === 'code') {
|
if (block.kind === 'text') {
|
||||||
|
emitBlock(lines, block);
|
||||||
|
} else if (block.kind === 'code') {
|
||||||
lines.push(`\`\`\`${block.language || ''}`, codeBlockText(block), '```', '');
|
lines.push(`\`\`\`${block.language || ''}`, codeBlockText(block), '```', '');
|
||||||
} else if (block.kind === 'table') {
|
} else if (block.kind === 'table') {
|
||||||
if (!block.rows || !block.rows.length) continue;
|
if (!block.rows || !block.rows.length) continue;
|
||||||
@@ -71,9 +74,6 @@ function exportMarkdown(ast, outDir, template = {}) {
|
|||||||
lines.push('');
|
lines.push('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emitBlocks(lines, step, 'after-description');
|
|
||||||
emitBlocks(lines, step, 'after-image');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const file = path.join(outDir, `${guideSlug(ast)}.md`);
|
const file = path.join(outDir, `${guideSlug(ast)}.md`);
|
||||||
@@ -81,8 +81,7 @@ function exportMarkdown(ast, outDir, template = {}) {
|
|||||||
return { file, imageCount: images.size };
|
return { file, imageCount: images.size };
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitBlocks(lines, step, position) {
|
function emitBlock(lines, tb) {
|
||||||
for (const tb of stepBlocks(step).filter((b) => b.kind === 'text' && b.position === position)) {
|
|
||||||
const label = LEVEL_LABEL[tb.level] || 'Note';
|
const label = LEVEL_LABEL[tb.level] || 'Note';
|
||||||
// GitHub-Flavored Markdown alert syntax — renders with a colored,
|
// GitHub-Flavored Markdown alert syntax — renders with a colored,
|
||||||
// icon-labeled box on GitHub/Azure DevOps wikis and several other
|
// icon-labeled box on GitHub/Azure DevOps wikis and several other
|
||||||
@@ -92,7 +91,6 @@ function emitBlocks(lines, step, position) {
|
|||||||
const body = htmlToMarkdown(tb.descriptionHtml);
|
const body = htmlToMarkdown(tb.descriptionHtml);
|
||||||
if (body) lines.push(`> ${body.replace(/\n/g, '\n> ')}`);
|
if (body) lines.push(`> ${body.replace(/\n/g, '\n> ')}`);
|
||||||
lines.push('');
|
lines.push('');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { exportMarkdown, DEFAULT_TEMPLATE, anchorFor };
|
module.exports = { exportMarkdown, DEFAULT_TEMPLATE, anchorFor };
|
||||||
|
|||||||
+8
-9
@@ -3,7 +3,7 @@
|
|||||||
const fs = require('node:fs');
|
const fs = require('node:fs');
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
const { PdfBuilder } = require('../core/pdf');
|
const { PdfBuilder } = require('../core/pdf');
|
||||||
const { guideSlug, renderAllImages, LEVEL_LABEL, stepBlocks, codeBlockText } = require('./common');
|
const { guideSlug, renderAllImages, LEVEL_LABEL, stepContentGroups, codeBlockText } = require('./common');
|
||||||
const { htmlToText } = require('../core/util');
|
const { htmlToText } = require('../core/util');
|
||||||
const { htmlToBlocks } = require('../core/htmlblocks');
|
const { htmlToBlocks } = require('../core/htmlblocks');
|
||||||
|
|
||||||
@@ -207,7 +207,8 @@ function exportPdf(ast, outDir, template = {}) {
|
|||||||
pdf.rect(M, y, usableW, 0.8, { fill: [225, 228, 232] });
|
pdf.rect(M, y, usableW, 0.8, { fill: [225, 228, 232] });
|
||||||
y += 8;
|
y += 8;
|
||||||
|
|
||||||
emitBlocks(step, 'before-description');
|
const { before, rest } = stepContentGroups(step);
|
||||||
|
for (const tb of before) emitBlock(tb);
|
||||||
if (step.descriptionHtml) writeDescription(step.descriptionHtml);
|
if (step.descriptionHtml) writeDescription(step.descriptionHtml);
|
||||||
|
|
||||||
const img = images.get(step.stepId);
|
const img = images.get(step.stepId);
|
||||||
@@ -221,8 +222,10 @@ function exportPdf(ast, outDir, template = {}) {
|
|||||||
y += h + 10;
|
y += h + 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const block of stepBlocks(step).filter((item) => item.kind !== 'text')) {
|
for (const block of rest) {
|
||||||
if (block.kind === 'code') {
|
if (block.kind === 'text') {
|
||||||
|
emitBlock(block);
|
||||||
|
} else if (block.kind === 'code') {
|
||||||
const lines = String(codeBlockText(block) || '').split('\n');
|
const lines = String(codeBlockText(block) || '').split('\n');
|
||||||
const lineH = 9 * 1.3;
|
const lineH = 9 * 1.3;
|
||||||
ensure(Math.min(lines.length, 4) * lineH + 12);
|
ensure(Math.min(lines.length, 4) * lineH + 12);
|
||||||
@@ -255,13 +258,10 @@ function exportPdf(ast, outDir, template = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emitBlocks(step, 'after-description');
|
|
||||||
emitBlocks(step, 'after-image');
|
|
||||||
y += 10;
|
y += 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitBlocks(step, position) {
|
function emitBlock(tb) {
|
||||||
for (const tb of stepBlocks(step).filter((b) => b.kind === 'text' && b.position === position)) {
|
|
||||||
const label = `${LEVEL_LABEL[tb.level] || 'Note'}${tb.title ? `: ${tb.title}` : ''}`;
|
const label = `${LEVEL_LABEL[tb.level] || 'Note'}${tb.title ? `: ${tb.title}` : ''}`;
|
||||||
const { items, height: bodyH } = tb.descriptionHtml
|
const { items, height: bodyH } = tb.descriptionHtml
|
||||||
? layoutDescription(pdf, tb.descriptionHtml, usableW - 18, 9.5)
|
? layoutDescription(pdf, tb.descriptionHtml, usableW - 18, 9.5)
|
||||||
@@ -294,7 +294,6 @@ function exportPdf(ast, outDir, template = {}) {
|
|||||||
}
|
}
|
||||||
y += blockH + 6;
|
y += blockH + 6;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fs.mkdirSync(outDir, { recursive: true });
|
fs.mkdirSync(outDir, { recursive: true });
|
||||||
const file = path.join(outDir, `${guideSlug(ast)}.pdf`);
|
const file = path.join(outDir, `${guideSlug(ast)}.pdf`);
|
||||||
|
|||||||
Reference in New Issue
Block a user