import { marked, type MarkedOptions } from 'marked';
/**
* Markdown 转 HTML 转换结果
*/
export interface MarkdownToHtmlResult {
/** 转换后的 HTML 字符串 */
html: string;
/** 原始 Markdown 文本长度 */
originalLength: number;
/** 生成的 HTML 长度 */
htmlLength: number;
/** 是否包含错误 */
hasError: boolean;
/** 错误信息(如果有) */
error?: string;
}
/**
* 默认的 Markdown 渲染选项配置
*/
const defaultMarkedOptions: MarkedOptions = {
gfm: true,
breaks: true,
};
/**
* 将 Markdown 文本转换为 HTML
*
* @param markdown - Markdown 源文本
* @returns MarkdownToHtmlResult 转换结果
*/
export function markdownToHtml(markdown: string): MarkdownToHtmlResult {
try {
const trimmed = markdown.trim();
if (!trimmed) {
return {
html: '',
originalLength: 0,
htmlLength: 0,
hasError: false,
};
}
const html = marked.parse(trimmed, { ...defaultMarkedOptions, async: false });
return {
html: html.trim(),
originalLength: markdown.length,
htmlLength: html.length,
hasError: false,
};
} catch (error) {
return {
html: '',
originalLength: markdown.length,
htmlLength: 0,
hasError: true,
error: error instanceof Error ? error.message : 'Markdown 解析失败',
};
}
}
/**
* 为 HTML 内容包装完整的文档结构(用于打印/下载)
*
* @param html - 主体 HTML 内容
* @param title - 文档标题
* @returns 完整的 HTML 文档字符串
*/
export function wrapHtmlDocument(html: string, title: string = 'Markdown Export'): string {
return `
${escapeHtml(title)}
${html}
`;
}
/**
* 转义 HTML 特殊字符
*/
function escapeHtml(text: string): string {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
/**
* 下载 HTML 文件
*
* @param content - 文件内容
* @param filename - 下载文件名
*/
export function downloadHtmlFile(content: string, filename: string = 'export.html'): void {
const blob = new Blob([content], { type: 'text/html;charset=utf-8' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
}
/**
* 打印 HTML 内容
*
* @param html - 要打印的 HTML 内容
* @param title - 打印窗口标题
*/
export function printHtml(html: string, title: string = 'Markdown Preview'): void {
const printWindow = window.open('', '_blank', 'width=800,height=600');
if (!printWindow) {
console.error('无法打开打印窗口,请检查浏览器弹窗拦截设置');
return;
}
const doc = wrapHtmlDocument(html, title);
printWindow.document.write(doc);
printWindow.document.close();
// 等待样式加载完成后打印
printWindow.onload = () => {
printWindow.print();
};
// 部分浏览器 onload 不触发,使用延迟回退
setTimeout(() => {
printWindow.print();
}, 500);
}
/**
* 示例 Markdown 文本(用于占位提示)
*/
export const SAMPLE_MARKDOWN = `# 欢迎使用 Markdown 转 HTML
这是一个 **Markdown** 编辑器,支持实时预览。
## 基础语法
### 标题
使用 \`#\` 符号表示不同级别的标题。
### 列表
- 无序列表项 1
- 无序列表项 2
- 嵌套列表项
1. 有序列表项 1
2. 有序列表项 2
### 文本样式
- **粗体文本**
- *斜体文本*
- ~~删除线文本~~
- \`行内代码\`
### 链接与图片
[访问 GitHub](https://github.com)
### 代码块
\`\`\`javascript
function hello() {
console.log('Hello, World!');
}
\`\`\`
## 扩展语法
### 表格
| 名称 | 类型 | 描述 |
|------|------|------|
| name | string | 用户名 |
| age | number | 年龄 |
### 任务列表
- [x] 已完成任务
- [ ] 待办任务
### 引用
> 这是一段引用文本。
> 可以有多行。
### 分割线
---
*开始编辑你的 Markdown 内容吧!*`;