update at 2025-10-16 16:10:58
This commit is contained in:
263
src/core/html-processor.ts
Normal file
263
src/core/html-processor.ts
Normal file
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* 文件: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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user