Files
note2any/src/core/html-processor.ts
2025-10-16 16:10:58 +08:00

263 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 文件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;
}
}
}