Render rich text, links, and special characters correctly in PDF exports
Template tests / tests (push) Successful in 1m40s
Template tests / tests (pull_request) Successful in 1m39s

PDF exports now preserve bold/italic/links/lists/blockquotes from the
description editor (via a new HTML block/run parser) instead of flattening
to plain text. Literal "[text](url)" links inserted by the editor's Link
button are converted to real <a> tags before rendering. Tabs and common
"smart typography" characters (smart quotes, dashes, ellipsis, bullet) are
mapped to their WinAnsiEncoding glyphs instead of rendering as "?".

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
2026-06-13 21:35:55 -05:00
co-authored by Claude Sonnet 4.6
parent 420b32c0f8
commit 0ead121327
5 changed files with 313 additions and 20 deletions
+15
View File
@@ -95,6 +95,20 @@ function clamp(v, min, max) {
return Math.min(max, Math.max(min, v));
}
// Matches literal markdown-style links typed in the description editor,
// e.g. "[Settings](https://example.com)". Excludes < > so it never spans
// across an existing HTML tag boundary.
const MD_LINK_RE = /\[([^[\]<>]*)\]\(([^()<>]+)\)/g;
/**
* Turn literal "[text](url)" markdown link syntax (as inserted by the
* description editor's Link button) into real <a href> tags, so exporters
* that consume HTML render an actual link instead of the raw brackets.
*/
function linkifyMarkdownLinks(html) {
return String(html || '').replace(MD_LINK_RE, (m, label, href) => `<a href="${escapeHtml(href)}">${label}</a>`);
}
/** Filesystem-safe slug for export folder names like steps-<title>. */
function slugify(text, fallback = 'untitled') {
const slug = String(text || '')
@@ -116,6 +130,7 @@ module.exports = {
readJsonSync,
readJsonIfExists,
htmlToText,
linkifyMarkdownLinks,
decodeEntities,
escapeHtml,
escapeXml,