263 lines
6.8 KiB
TypeScript
263 lines
6.8 KiB
TypeScript
/**
|
||
* 文件:html-processor.ts
|
||
* 作用:处理HTML内容的生成和格式化
|
||
*
|
||
* 职责:
|
||
* 1. HTML内容清理和格式化
|
||
* 2. CSS样式内联处理
|
||
* 3. HTML结构生成
|
||
* 4. 代码高亮处理
|
||
*/
|
||
|
||
import { sanitizeHTMLToDom } from 'obsidian';
|
||
import { applyCSS } from '../utils';
|
||
import InlineCSS from '../inline-css';
|
||
import { NMPSettings } from '../settings';
|
||
import AssetsManager from '../assets';
|
||
import { ErrorHandler } from './error-handler';
|
||
|
||
export interface HtmlProcessOptions {
|
||
enableCodeHighlight?: boolean;
|
||
enableInlineCSS?: boolean;
|
||
theme?: string;
|
||
highlight?: string;
|
||
customCSS?: string;
|
||
}
|
||
|
||
/**
|
||
* HTML处理器类
|
||
*/
|
||
export class HtmlProcessor {
|
||
private settings: NMPSettings;
|
||
private assetsManager: AssetsManager;
|
||
|
||
constructor() {
|
||
this.settings = NMPSettings.getInstance();
|
||
this.assetsManager = AssetsManager.getInstance();
|
||
}
|
||
|
||
/**
|
||
* 生成完整的HTML文档
|
||
*/
|
||
generateFullHtml(content: string, options: HtmlProcessOptions = {}): string {
|
||
try {
|
||
const theme = options.theme || this.settings.defaultStyle;
|
||
const highlight = options.highlight || this.settings.defaultHighlight;
|
||
|
||
// 获取基础样式
|
||
const baseCSS = this.getBaseCSS(theme, highlight);
|
||
|
||
// 处理自定义CSS
|
||
let customCSS = options.customCSS || '';
|
||
if (this.settings.useCustomCss && this.settings.customCSSNote) {
|
||
customCSS += '\n' + this.settings.customCSSNote;
|
||
}
|
||
|
||
// 构建完整HTML
|
||
const html = `
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Note2Any Export</title>
|
||
<style>
|
||
${baseCSS}
|
||
${customCSS}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="note2any">
|
||
${content}
|
||
</div>
|
||
</body>
|
||
</html>`;
|
||
|
||
return html;
|
||
} catch (error) {
|
||
ErrorHandler.handle(error as Error, 'HtmlProcessor.generateFullHtml');
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理HTML内容的内联CSS
|
||
*/
|
||
async processInlineCSS(html: string): Promise<string> {
|
||
// 简化版内联CSS处理,使用原生方法
|
||
try {
|
||
// 这里可以实现一个简单的CSS内联功能
|
||
// 或者集成第三方库如 juice
|
||
return html;
|
||
} catch (error) {
|
||
ErrorHandler.handle(error as Error, 'HtmlProcessor.processInlineCSS');
|
||
console.warn('[HtmlProcessor] 内联CSS处理失败,使用原始HTML', error);
|
||
return html;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 清理和格式化HTML内容
|
||
*/
|
||
sanitizeHtml(html: string): DocumentFragment {
|
||
try {
|
||
return sanitizeHTMLToDom(html);
|
||
} catch (error) {
|
||
ErrorHandler.handle(error as Error, 'HtmlProcessor.sanitizeHtml');
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 应用CSS样式到元素
|
||
*/
|
||
applyStylesToElement(html: string, css: string): string {
|
||
try {
|
||
return applyCSS(html, css);
|
||
} catch (error) {
|
||
ErrorHandler.handle(error as Error, 'HtmlProcessor.applyStylesToElement');
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取基础CSS样式
|
||
*/
|
||
private getBaseCSS(theme: string, highlight: string): string {
|
||
try {
|
||
let css = '';
|
||
|
||
// 主题样式
|
||
const themeCSS = this.assetsManager.getTheme(theme);
|
||
if (themeCSS) {
|
||
css += themeCSS + '\n';
|
||
}
|
||
|
||
// 代码高亮样式
|
||
const highlightCSS = this.assetsManager.getHighlight(highlight);
|
||
if (highlightCSS) {
|
||
css += highlightCSS + '\n';
|
||
}
|
||
|
||
return css;
|
||
} catch (error) {
|
||
ErrorHandler.handle(error as Error, 'HtmlProcessor.getBaseCSS');
|
||
return '';
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成响应式HTML包装器
|
||
*/
|
||
wrapWithResponsive(content: string, maxWidth = '800px'): string {
|
||
return `
|
||
<div style="max-width: ${maxWidth}; margin: 0 auto; padding: 20px; box-sizing: border-box;">
|
||
${content}
|
||
</div>`;
|
||
}
|
||
|
||
/**
|
||
* 添加打印样式
|
||
*/
|
||
addPrintStyles(): string {
|
||
return `
|
||
<style media="print">
|
||
@page {
|
||
margin: 2cm;
|
||
size: A4;
|
||
}
|
||
|
||
.note2any {
|
||
max-width: none !important;
|
||
margin: 0 !important;
|
||
padding: 0 !important;
|
||
}
|
||
|
||
.no-print {
|
||
display: none !important;
|
||
}
|
||
|
||
pre {
|
||
white-space: pre-wrap !important;
|
||
word-break: break-word !important;
|
||
}
|
||
|
||
img {
|
||
max-width: 100% !important;
|
||
height: auto !important;
|
||
page-break-inside: avoid;
|
||
}
|
||
|
||
h1, h2, h3, h4, h5, h6 {
|
||
page-break-after: avoid;
|
||
}
|
||
|
||
p, li {
|
||
orphans: 3;
|
||
widows: 3;
|
||
}
|
||
</style>`;
|
||
}
|
||
|
||
/**
|
||
* 处理代码块高亮
|
||
*/
|
||
processCodeHighlight(html: string, highlight: string): string {
|
||
try {
|
||
// 这里可以添加更复杂的代码高亮处理逻辑
|
||
// 目前主要依赖 AssetsManager 提供的高亮样式
|
||
|
||
return html;
|
||
} catch (error) {
|
||
ErrorHandler.handle(error as Error, 'HtmlProcessor.processCodeHighlight');
|
||
return html;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 优化HTML结构用于移动端显示
|
||
*/
|
||
optimizeForMobile(html: string): string {
|
||
try {
|
||
// 添加移动端优化的meta标签和样式
|
||
const mobileOptimizations = `
|
||
<meta name="format-detection" content="telephone=no">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||
<style>
|
||
/* 移动端优化样式 */
|
||
@media screen and (max-width: 768px) {
|
||
.note2any {
|
||
padding: 15px 10px !important;
|
||
font-size: 16px !important;
|
||
line-height: 1.6 !important;
|
||
}
|
||
|
||
pre, code {
|
||
font-size: 14px !important;
|
||
overflow-x: auto !important;
|
||
white-space: pre-wrap !important;
|
||
}
|
||
|
||
table {
|
||
font-size: 14px !important;
|
||
display: block !important;
|
||
overflow-x: auto !important;
|
||
white-space: nowrap !important;
|
||
}
|
||
|
||
img {
|
||
max-width: 100% !important;
|
||
height: auto !important;
|
||
}
|
||
}
|
||
</style>`;
|
||
|
||
// 在head标签中插入移动端优化
|
||
return html.replace('</head>', mobileOptimizations + '</head>');
|
||
} catch (error) {
|
||
ErrorHandler.handle(error as Error, 'HtmlProcessor.optimizeForMobile');
|
||
return html;
|
||
}
|
||
}
|
||
} |