diff --git a/package.json b/package.json index a90bb16..c71d5c0 100644 --- a/package.json +++ b/package.json @@ -260,6 +260,11 @@ "description": "%print.markdown.watermark.enable%", "default": false }, + "print.markdown.showFrontmatter": { + "type": "boolean", + "description": "%print.markdown.showFrontmatter%", + "default": true + }, "print.plaintext.stylesheets": { "type": "array", "items": { diff --git a/package.nls.json b/package.nls.json index f71373d..cbcfdc6 100644 --- a/package.nls.json +++ b/package.nls.json @@ -34,6 +34,7 @@ "print.markdown.stylesheets": "URLs or paths for CSS for rendered Markdown. For workspace relative paths, use 'workspace.resource/path/to/your.css'. For absolute filesystem paths, use 'absolute/path/to/your.css'. Otherwise, paths are relative to the base document ('./my.css' would be in the same folder as the document), or absolute URLs (https://...). For folders or multi-file selections use absolute, absolute filesystem paths, or workspace-relative paths.", "print.markdown.smartQuotes.enable": "Enable the use of smart quotes in rendered Markdown.", "print.markdown.watermark.enable": "Enable embedded watermark text", + "print.markdown.showFrontmatter": "Show frontmatter metadata as a table at the top of printed documents", "print.markdown.watermark.text": "Watermark text to be periodically embedded in the rendered document", "print.plaintext.stylesheets": "URLs or paths for extra CSS to be applied to plaintext. For workspace relative paths, use 'workspace.resource/path/to/your.css'. For absolute filesystem paths, use 'absolute/path/to/your.css'. Otherwise, paths are relative to the base document ('./my.css' would be in the same folder as the document), or absolute URLs (https://...). For folders or multi-file selections use absolute, absolute filesystem paths, or workspace-relative paths.", "print.sourcecode.colourScheme.markdownDescription": "[Stylesheet preview is available online](https://highlightjs.org/demo/). Only 'light' versions are offered because paper is white.", diff --git a/src/renderers/frontmatter.ts b/src/renderers/frontmatter.ts new file mode 100644 index 0000000..bc6a65d --- /dev/null +++ b/src/renderers/frontmatter.ts @@ -0,0 +1,116 @@ +import * as yaml from "yaml"; +import { logger } from '../logger'; + +/** + * Represents the result of extracting frontmatter from markdown content + */ +export interface FrontmatterResult { + frontmatter: any | null; + content: string; +} + +/** + * HTML escaping utility function for frontmatter values + */ +function escapeHtml(text: string): string { + return text.replace(/&/g, '&').replace(//g, '>'); +} + +/** + * Extracts frontmatter from markdown content + * Supports both YAML (---) and TOML (+++) delimiters + * + * @param raw The raw markdown content + * @returns Object containing parsed frontmatter and remaining content + */ +export function extractFrontmatter(raw: string): FrontmatterResult { + // Check if content starts with frontmatter delimiters (--- or +++) + const yamlDelimiterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n/; + const tomlDelimiterRegex = /^\+\+\+\s*\n([\s\S]*?)\n\+\+\+\s*\n/; + + let frontmatterMatch = raw.match(yamlDelimiterRegex); + let frontmatterContent = ''; + let isToml = false; + + if (!frontmatterMatch) { + frontmatterMatch = raw.match(tomlDelimiterRegex); + isToml = true; + } + + if (frontmatterMatch) { + frontmatterContent = frontmatterMatch[1]; + const content = raw.substring(frontmatterMatch[0].length); + + try { + let frontmatterData; + if (isToml) { + // For TOML, we'll try to parse it as YAML for now + // You might want to add a TOML parser like @iarna/toml if needed + frontmatterData = yaml.parse(frontmatterContent); + } else { + frontmatterData = yaml.parse(frontmatterContent); + } + + return { frontmatter: frontmatterData, content }; + } catch (error) { + logger.warn(`Failed to parse frontmatter: ${error}`); + return { frontmatter: null, content: raw }; + } + } + + return { frontmatter: null, content: raw }; +} + +/** + * Converts frontmatter object to HTML table string + * + * @param frontmatter The parsed frontmatter object + * @returns HTML string representing the frontmatter as a table + */ +export function frontmatterToTable(frontmatter: any): string { + if (!frontmatter || typeof frontmatter !== 'object') { + return ''; + } + + const rows = Object.entries(frontmatter).map(([key, value]) => { + let displayValue = ''; + if (value === null || value === undefined) { + displayValue = ''; + } else if (Array.isArray(value)) { + displayValue = value.join(', '); + } else if (typeof value === 'object') { + displayValue = JSON.stringify(value, null, 2); + } else { + displayValue = String(value); + } + + return `