diff --git a/index.html b/index.html index 550fee4..4ffa716 100644 --- a/index.html +++ b/index.html @@ -51,9 +51,10 @@ - + +
diff --git a/script.js b/script.js index 308211b..cd33aa4 100644 --- a/script.js +++ b/script.js @@ -220,7 +220,64 @@ document.addEventListener("DOMContentLoaded", function () { }); } - const sampleMarkdown = `# Welcome to Markdown Viewer + function parseFrontmatter(markdown) { + const match = markdown.match(/^---\r?\n([\s\S]*?)\r?\n---(\r?\n|$)/); + if (!match) return { frontmatter: null, body: markdown }; + try { + const data = jsyaml.load(match[1]) || {}; + return { frontmatter: data, body: markdown.slice(match[0].length) }; + } catch (e) { + console.warn('Frontmatter YAML parse error:', e); + return { frontmatter: null, body: markdown }; + } + } + + function renderFrontmatterValue(value) { + if (value === null || value === undefined) return ''; + if (value instanceof Date) { + const y = value.getUTCFullYear(); + const m = String(value.getUTCMonth() + 1).padStart(2, '0'); + const d = String(value.getUTCDate()).padStart(2, '0'); + return `${y}-${m}-${d}`; + } + if (Array.isArray(value)) { + const allPrimitive = value.every(v => v === null || typeof v !== 'object'); + if (allPrimitive) { + return value + .map(v => `${escapeHtml(String(v ?? ''))}`) + .join(''); + } + return `
${escapeHtml(jsyaml.dump(value).trimEnd())}
`; + } + if (typeof value === 'object') { + return `
${escapeHtml(jsyaml.dump(value).trimEnd())}
`; + } + return escapeHtml(String(value)); + } + + function renderFrontmatterTable(data) { + const rows = Object.entries(data).map(([key, value]) => + `${escapeHtml(key)}${renderFrontmatterValue(value)}` + ); + return `${rows.join('')}
`; + } + + function escapeHtml(str) { + return str + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + } + + const sampleMarkdown = `--- +title: Welcome to Markdown Viewer +description: A GitHub-style Markdown renderer with live preview, math, diagrams, and export support. +author: ThisIs-Developer +tags: ["markdown", "preview", "mermaid", "latex", "open-source"] +--- + +# Welcome to Markdown Viewer ## ✨ Key Features - **Live Preview** with GitHub styling @@ -853,8 +910,9 @@ This is a fully client-side application. Your content never leaves your browser function renderMarkdown() { try { - const markdown = markdownEditor.value; - const html = marked.parse(markdown); + const { frontmatter, body } = parseFrontmatter(markdownEditor.value); + const tableHtml = frontmatter ? renderFrontmatterTable(frontmatter) : ''; + const html = tableHtml + marked.parse(body); const sanitizedHtml = DOMPurify.sanitize(html, { ADD_TAGS: ['mjx-container'], ADD_ATTR: ['id', 'class', 'style'] diff --git a/styles.css b/styles.css index 258420e..7795594 100644 --- a/styles.css +++ b/styles.css @@ -1883,3 +1883,65 @@ a:focus { width: 100%; } } + +.frontmatter-table { + border-collapse: collapse; + margin-bottom: 1.5em; + font-size: 0.9em; + width: auto; + max-width: 100%; +} + +.frontmatter-table th, +.frontmatter-table td { + border: 1px solid var(--border-color); + padding: 6px 13px; + vertical-align: top; + color: var(--text-color); +} + +.frontmatter-table tr:nth-child(odd) th, +.frontmatter-table tr:nth-child(odd) td { + background-color: var(--table-bg); +} + +.frontmatter-table tr:nth-child(even) th, +.frontmatter-table tr:nth-child(even) td { + background-color: var(--editor-bg); +} + +.frontmatter-table th { + font-weight: 600; + text-align: right; + white-space: nowrap; + vertical-align: middle; +} + +.frontmatter-table td { + text-align: left; +} + +.fm-complex { + margin: 0; + padding: 4px 6px; + font-size: 0.8em; + font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace; + white-space: pre-wrap; + word-break: break-word; + background: transparent; + border: none; + color: var(--text-color); +} + +.fm-tag { + display: inline-block; + padding: 2px 8px; + margin: 2px 3px 2px 0; + border: 1px solid var(--border-color); + border-radius: 2em; + font-size: 0.8em; + font-weight: 500; + color: var(--accent-color); + background-color: var(--button-bg); + white-space: nowrap; +}