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 `
`;
+ }
+
+ 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;
+}