Files
note2any/src/xiaohongshu/xhs-preview.ts
2025-10-21 21:47:02 +08:00

769 lines
28 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.

/**
* 文件xiaohongshu/xhs-preview.ts
* 作用:小红书预览视图组件,专门处理小红书平台的预览、分页和切图功能
*
* 功能:
* 1. 渲染小红书专属的预览界面(顶部工具栏、分页导航、底部切图按钮)
* 2. 处理文章内容的小红书格式化和分页
* 3. 提供切图功能(当前页/全部页)
* 4. 管理小红书特有的样式和字体设置
*/
import { Notice, TFile } from 'obsidian';
import { NMPSettings } from '../settings';
import AssetsManager from '../assets';
import { paginateArticle, renderPage, PageInfo } from './paginator';
import { sliceCurrentPage, sliceAllPages } from './slice';
const XHS_PREVIEW_DEFAULT_WIDTH = 540;
const XHS_PREVIEW_WIDTH_OPTIONS = [1080, 720, 540, 360];
// 字号控制常量:一处修改即可同步 UI 显示、输入校验和渲染逻辑
const XHS_FONT_SIZE_MIN = 18;
const XHS_FONT_SIZE_MAX = 45;
const XHS_FONT_SIZE_DEFAULT = 36;
/**
* 小红书预览视图类
*/
export class XiaohongshuPreview {
container: HTMLElement;
settings: NMPSettings;
assetsManager: AssetsManager;
app: any;
currentFile: TFile | null = null;
// UI 元素
templateSelect!: HTMLSelectElement;
fontSizeInput!: HTMLInputElement;
previewWidthSelect!: HTMLSelectElement;
themeSelect!: HTMLSelectElement;
platformSelect: HTMLSelectElement | null = null;
pageContainer!: HTMLDivElement;
pageNumberInput!: HTMLInputElement;
pageTotalLabel!: HTMLSpanElement;
styleEl: HTMLStyleElement | null = null; // 主题样式注入节点
currentThemeClass: string = '';
// 分页数据
pages: PageInfo[] = [];
currentPageIndex: number = 0;
currentFontSize: number = XHS_FONT_SIZE_DEFAULT;
articleHTML: string = '';
// 回调函数
onRefreshCallback?: () => Promise<void>;
onPublishCallback?: () => Promise<void>;
onPlatformChangeCallback?: (platform: string) => Promise<void>;
constructor(container: HTMLElement, app: any) {
this.container = container;
this.app = app;
this.settings = NMPSettings.getInstance();
this.assetsManager = AssetsManager.getInstance();
}
/**
* 构建完整的小红书预览界面
*/
build(): void {
this.container.empty();
this.container.addClass('note2any-platform-container');
// 准备样式挂载节点
if (!this.styleEl) {
this.styleEl = document.createElement('style');
this.styleEl.setAttr('data-xhs-style', '');
}
if (!this.container.contains(this.styleEl)) {
this.container.appendChild(this.styleEl);
}
// 顶部平台选择器栏
const header = this.container.createDiv({ cls: 'note2any-platform-header' });
// 平台选择器区域
const platformSelector = header.createDiv({ cls: 'note2any-platform-selector' });
const platformLabel = platformSelector.createDiv({
cls: 'note2any-platform-label',
text: '发布平台'
});
// 使用真实的 select 元素,直接应用样式
this.platformSelect = platformSelector.createEl('select', { cls: 'note2any-select note2any-platform-select' }) as HTMLSelectElement;
const wechatOption = this.platformSelect.createEl('option');
wechatOption.value = 'wechat';
wechatOption.text = '📱 公众号';
const xhsOption = this.platformSelect.createEl('option');
xhsOption.value = 'xiaohongshu';
xhsOption.text = '📔 小红书';
// 设置默认选中为小红书
this.platformSelect.value = 'xiaohongshu';
this.platformSelect.onchange = () => {
if (this.platformSelect && this.platformSelect.value === 'wechat' && this.onPlatformChangeCallback) {
this.onPlatformChangeCallback('wechat');
}
};
// 按钮组
const buttonGroup = header.createDiv({ cls: 'note2any-button-group' });
const refreshBtn = buttonGroup.createEl('button', {
text: '刷新',
cls: 'note2any-button'
});
refreshBtn.onclick = () => this.refresh();
const publishBtn = buttonGroup.createEl('button', {
text: '发布',
cls: 'note2any-button'
});
publishBtn.onclick = () => this.publish();
const accessBtn = buttonGroup.createEl('button', {
text: '访问',
cls: 'note2any-button'
});
accessBtn.onclick = () => {
// TODO: 实现小红书访问逻辑
new Notice('小红书访问功能待实现');
};
// 控制栏 (账号、主题、宽度)
const controlsRow = this.container.createDiv({ cls: 'note2any-controls-row' });
// 账号字段
const accountField = controlsRow.createDiv({ cls: 'note2any-field note2any-field-account' });
accountField.createDiv({ cls: 'note2any-field-label', text: '账号' });
const accountSelect = accountField.createEl('select', { cls: 'note2any-select' });
const accountOption = accountSelect.createEl('option');
accountOption.value = 'default';
accountOption.text = 'Value';
// 主题字段
const themeField = controlsRow.createDiv({ cls: 'note2any-field note2any-field-theme' });
themeField.createDiv({ cls: 'note2any-field-label', text: '主题' });
this.themeSelect = themeField.createEl('select', { cls: 'note2any-select' }) as HTMLSelectElement;
this.themeSelect.onchange = async () => {
this.settings.defaultStyle = this.themeSelect.value;
this.applyThemeCSS();
await this.repaginateAndRender();
const plugin = (this.app as any)?.plugins?.getPlugin?.('note2any');
if (plugin?.saveSettings) {
await plugin.saveSettings();
}
};
for (let theme of this.assetsManager.themes) {
const option = this.themeSelect.createEl('option');
option.value = theme.className;
option.text = theme.name;
option.selected = theme.className === this.settings.defaultStyle;
}
// 宽度字段
const widthField = controlsRow.createDiv({ cls: 'note2any-field note2any-field-width' });
widthField.createDiv({ cls: 'note2any-field-label', text: '宽度' });
const currentPreviewWidth = this.settings.xhsPreviewWidth || XHS_PREVIEW_DEFAULT_WIDTH;
this.previewWidthSelect = widthField.createEl('select', {
cls: 'note2any-select'
}) as HTMLSelectElement;
// 添加宽度选项
const widthOptions = [360, 540, 720];
for (let width of widthOptions) {
const option = this.previewWidthSelect.createEl('option');
option.value = String(width);
option.text = `${width}px`;
option.selected = width === currentPreviewWidth;
}
this.previewWidthSelect.onchange = async () => {
const newWidth = parseInt(this.previewWidthSelect.value);
this.settings.xhsPreviewWidth = newWidth;
await this.repaginateAndRender();
const plugin = (this.app as any)?.plugins?.getPlugin?.('note2any');
if (plugin?.saveSettings) {
await plugin.saveSettings();
}
};
// 内容区域
this.pageContainer = this.container.createDiv({ cls: 'note2any-content-area' });
// 底部工具栏
const bottomToolbar = this.container.createDiv({ cls: 'note2any-bottom-toolbar' });
// 当前图按钮
const sliceCurrentBtn = bottomToolbar.createEl('button', {
text: '当前图',
cls: 'note2any-slice-button'
});
sliceCurrentBtn.onclick = () => this.sliceCurrentPage();
// 字体大小控制
const fontSizeControl = bottomToolbar.createDiv({ cls: 'note2any-fontsize-control' });
// 字体大小下拉选择器
const fontSizeSelectWrapper = fontSizeControl.createDiv({ cls: 'note2any-fontsize-select-wrapper' });
const fontSizeSelect = fontSizeSelectWrapper.createEl('select', {
cls: 'note2any-fontsize-select'
}) as HTMLSelectElement;
// 添加字体大小选项 (30-40)
for (let size = XHS_FONT_SIZE_MIN; size <= XHS_FONT_SIZE_MAX; size++) {
const option = fontSizeSelect.createEl('option');
option.value = String(size);
option.text = String(size);
option.selected = size === XHS_FONT_SIZE_DEFAULT;
}
fontSizeSelect.onchange = async () => {
this.currentFontSize = parseInt(fontSizeSelect.value);
this.fontSizeInput.value = String(this.currentFontSize);
await this.repaginateAndRender();
};
const stepper = fontSizeControl.createDiv({ cls: 'note2any-stepper' });
const decreaseBtn = stepper.createEl('button', {
text: '',
cls: 'note2any-stepper-button'
});
decreaseBtn.onclick = async () => {
if (this.currentFontSize > XHS_FONT_SIZE_MIN) {
this.currentFontSize--;
fontSizeSelect.value = String(this.currentFontSize);
this.fontSizeInput.value = String(this.currentFontSize);
await this.repaginateAndRender();
}
};
stepper.createDiv({ cls: 'note2any-stepper-separator' });
const increaseBtn = stepper.createEl('button', {
text: '',
cls: 'note2any-stepper-button'
});
increaseBtn.onclick = async () => {
if (this.currentFontSize < XHS_FONT_SIZE_MAX) {
this.currentFontSize++;
fontSizeSelect.value = String(this.currentFontSize);
this.fontSizeInput.value = String(this.currentFontSize);
await this.repaginateAndRender();
}
};
// 隐藏的字体输入框 (保留以兼容现有逻辑)
this.fontSizeInput = fontSizeControl.createEl('input', {
attr: {
type: 'number',
min: String(XHS_FONT_SIZE_MIN),
max: String(XHS_FONT_SIZE_MAX),
value: String(XHS_FONT_SIZE_DEFAULT),
style: 'display: none;'
}
});
this.fontSizeInput.onchange = () => {
this.currentFontSize = parseInt(this.fontSizeInput.value);
fontSizeSelect.value = String(this.currentFontSize);
};
// 分页控制
const pagination = bottomToolbar.createDiv({ cls: 'note2any-pagination' });
pagination.createDiv({ cls: 'note2any-pagination-separator' });
const prevBtn = pagination.createEl('button', {
cls: 'note2any-pagination-button',
attr: { 'aria-label': '上一页' }
});
prevBtn.innerHTML = '<svg width="32" height="32" viewBox="0 0 32 32"><path d="M20 8 L12 16 L20 24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>';
prevBtn.onclick = () => this.previousPage();
const pageCurrent = pagination.createEl('input', {
cls: 'note2any-pagination-current',
type: 'text',
value: '1',
attr: {
'inputmode': 'numeric',
'pattern': '[0-9]*'
}
});
// 处理页码输入 - 回车跳转
pageCurrent.addEventListener('keydown', (e: KeyboardEvent) => {
if (e.key === 'Enter') {
const targetPage = parseInt(pageCurrent.value);
if (!isNaN(targetPage) && targetPage >= 1 && targetPage <= this.pages.length) {
this.currentPageIndex = targetPage - 1;
this.renderCurrentPage();
} else {
// 恢复当前页码
pageCurrent.value = String(this.currentPageIndex + 1);
}
pageCurrent.blur();
}
});
// 失焦时跳转
pageCurrent.addEventListener('blur', () => {
const targetPage = parseInt(pageCurrent.value);
if (!isNaN(targetPage) && targetPage >= 1 && targetPage <= this.pages.length) {
this.currentPageIndex = targetPage - 1;
this.renderCurrentPage();
} else {
// 恢复当前页码
pageCurrent.value = String(this.currentPageIndex + 1);
}
});
// 聚焦时全选文本,方便输入
pageCurrent.addEventListener('focus', () => {
pageCurrent.select();
});
// 存储引用以便在其他地方更新显示
(this as any).pageCurrentDisplay = pageCurrent;
pagination.createDiv({
cls: 'note2any-pagination-separator-text',
text: '/'
});
this.pageTotalLabel = pagination.createEl('span', {
cls: 'note2any-pagination-total',
text: '68'
});
const nextBtn = pagination.createEl('button', {
cls: 'note2any-pagination-button',
attr: { 'aria-label': '下一页' }
});
nextBtn.innerHTML = '<svg width="32" height="32" viewBox="0 0 32 32"><path d="M12 8 L20 16 L12 24" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg>';
nextBtn.onclick = () => this.nextPage();
// 将可见的页码输入框设为主输入框
this.pageNumberInput = pageCurrent as HTMLInputElement;
// 存储显示元素引用以便更新
(this as any).pageCurrentDisplay = pageCurrent;
// 全部图按钮
const sliceAllBtn = bottomToolbar.createEl('button', {
text: '全部图',
cls: 'note2any-slice-button'
});
sliceAllBtn.onclick = () => this.sliceAllPages();
// 模板选择器 (隐藏,保留以兼容)
this.templateSelect = this.container.createEl('select', {
attr: { style: 'display: none;' }
});
['默认模板', '简约模板', '杂志模板'].forEach(name => {
const option = this.templateSelect.createEl('option');
option.value = name;
option.text = name;
});
}
/**
* 渲染文章内容并分页
*/
async renderArticle(articleHTML: string, file: TFile): Promise<void> {
this.articleHTML = articleHTML;
this.currentFile = file;
//new Notice('正在分页...');
// 创建临时容器用于分页
const tempContainer = document.createElement('div');
tempContainer.innerHTML = articleHTML;
tempContainer.style.width = `${this.settings.sliceImageWidth}px`;
tempContainer.classList.add('note2any');
if (this.currentThemeClass) {
tempContainer.classList.add(this.currentThemeClass);
}
tempContainer.style.fontSize = `${this.currentFontSize}px`;
document.body.appendChild(tempContainer);
try {
// 在分页前先应用主题与高亮,确保测量使用正确样式
this.applyThemeCSS();
this.pages = await paginateArticle(tempContainer, this.settings);
//new Notice(`分页完成:共 ${this.pages.length} 页`);
this.currentPageIndex = 0;
// 初次渲染时应用当前主题
this.renderCurrentPage();
} finally {
document.body.removeChild(tempContainer);
}
}
/**
* 渲染当前页
*/
private renderCurrentPage(): void {
if (this.pages.length === 0) return;
const page = this.pages[this.currentPageIndex];
this.pageContainer.empty();
// 重置滚动位置到顶部
this.pageContainer.scrollTop = 0;
// 创建包裹器,为缩放后的页面预留正确的布局空间
const wrapper = this.pageContainer.createDiv({ cls: 'xhs-page-wrapper' });
const classes = ['xhs-page'];
if (this.currentThemeClass) classes.push('note2any');
const pageElement = wrapper.createDiv({ cls: classes.join(' ') });
renderPage(pageElement, page.content, this.settings);
this.applyPreviewSizing(wrapper, pageElement);
// 应用字体设置
this.applyFontSettings(pageElement);
// 更新页码显示
this.updatePageNumberDisplay();
}
private updatePageNumberDisplay(): void {
if (!this.pageNumberInput || !this.pageTotalLabel) return;
const total = this.pages.length;
const pageCurrentDisplay = (this as any).pageCurrentDisplay;
if (total === 0) {
this.pageNumberInput.value = '0';
this.pageTotalLabel.innerText = '0';
if (pageCurrentDisplay) pageCurrentDisplay.textContent = '0';
return;
}
const current = Math.min(this.currentPageIndex + 1, total);
this.pageNumberInput.value = String(current);
this.pageTotalLabel.innerText = String(total);
if (pageCurrentDisplay) pageCurrentDisplay.textContent = String(current);
}
private handlePageNumberInput(): void {
if (!this.pageNumberInput) return;
const total = this.pages.length;
if (total === 0) {
this.pageNumberInput.value = '0';
if (this.pageTotalLabel) this.pageTotalLabel.innerText = '/0';
return;
}
const raw = this.pageNumberInput.value.trim();
if (raw.length === 0) {
this.updatePageNumberDisplay();
return;
}
const parsed = parseInt(raw, 10);
if (!Number.isFinite(parsed)) {
this.updatePageNumberDisplay();
return;
}
const target = Math.min(Math.max(parsed, 1), total) - 1;
if (target !== this.currentPageIndex) {
this.currentPageIndex = target;
this.renderCurrentPage();
} else {
this.updatePageNumberDisplay();
}
}
/**
* 根据设置的宽度和横竖比应用预览尺寸与缩放
*/
private applyPreviewSizing(wrapper: HTMLElement, pageElement: HTMLElement): void {
const configuredWidth = this.settings.sliceImageWidth || 1080;
const actualWidth = Math.max(1, configuredWidth);
const ratio = this.parseAspectRatio(this.settings.sliceImageAspectRatio);
const actualHeight = Math.round((actualWidth * ratio.height) / ratio.width);
const previewWidthSetting = this.settings.xhsPreviewWidth || XHS_PREVIEW_DEFAULT_WIDTH;
const previewWidth = Math.max(1, previewWidthSetting);
const scale = Math.max(previewWidth / actualWidth, 0.01);
const previewHeight = Math.max(1, Math.round(actualHeight * scale));
wrapper.style.width = `${previewWidth}px`;
wrapper.style.height = `${previewHeight}px`;
pageElement.style.width = `${actualWidth}px`;
pageElement.style.height = `${actualHeight}px`;
pageElement.style.transform = `scale(${scale})`;
pageElement.style.transformOrigin = 'center center';
pageElement.style.position = 'absolute';
pageElement.style.top = '50%';
pageElement.style.left = '50%';
pageElement.style.transform = `translate(-50%, -50%) scale(${scale})`;
}
private async onPreviewWidthChanged(newWidth: number): Promise<void> {
if (newWidth <= 0) return;
if (this.settings.xhsPreviewWidth === newWidth) return;
this.settings.xhsPreviewWidth = newWidth;
await this.persistSettings();
this.renderCurrentPage();
}
/**
* 解析横竖比例字符串
*/
private parseAspectRatio(ratio: string | undefined): { width: number; height: number } {
const parts = (ratio ?? '').split(':').map(part => parseFloat(part.trim()));
if (parts.length === 2 && isFinite(parts[0]) && isFinite(parts[1]) && parts[0] > 0 && parts[1] > 0) {
return { width: parts[0], height: parts[1] };
}
return { width: 3, height: 4 };
}
/**
* 应用字体设置(仅字号,字体从主题读取)
*/
private applyFontSettings(element: HTMLElement): void {
element.style.fontSize = `${this.currentFontSize}px`;
}
/**
* 切换字号(± 按钮)
*/
private async changeFontSize(delta: number): Promise<void> {
this.currentFontSize = Math.max(XHS_FONT_SIZE_MIN, Math.min(XHS_FONT_SIZE_MAX, this.currentFontSize + delta));
this.fontSizeInput.value = String(this.currentFontSize);
await this.repaginateAndRender();
}
/**
* 字号输入框改变事件
*/
private async onFontSizeInputChanged(): Promise<void> {
const val = parseInt(this.fontSizeInput.value, 10);
if (isNaN(val) || val < XHS_FONT_SIZE_MIN || val > XHS_FONT_SIZE_MAX) {
this.fontSizeInput.value = String(this.currentFontSize);
new Notice(`字号范围: ${XHS_FONT_SIZE_MIN}-${XHS_FONT_SIZE_MAX}`);
return;
}
this.currentFontSize = val;
await this.repaginateAndRender();
}
/**
* 上一页
*/
private previousPage(): void {
if (this.currentPageIndex > 0) {
this.currentPageIndex--;
this.renderCurrentPage();
}
}
/**
* 下一页
*/
private nextPage(): void {
if (this.currentPageIndex < this.pages.length - 1) {
this.currentPageIndex++;
this.renderCurrentPage();
}
}
/**
* 当前页切图
*/
private async sliceCurrentPage(): Promise<void> {
if (!this.currentFile) {
new Notice('请先打开一个笔记');
return;
}
const pageElement = this.pageContainer.querySelector('.xhs-page') as HTMLElement;
if (!pageElement) {
new Notice('未找到页面元素');
return;
}
new Notice('正在切图...');
try {
await sliceCurrentPage(pageElement, this.currentFile, this.currentPageIndex, this.app);
new Notice('✅ 当前页切图完成');
} catch (error) {
console.error('切图失败:', error);
new Notice('❌ 切图失败: ' + (error instanceof Error ? error.message : String(error)));
}
}
/**
* 刷新按钮点击
*/
private async onRefresh(): Promise<void> {
if (this.onRefreshCallback) {
await this.onRefreshCallback();
}
}
/**
* 发布按钮点击
*/
private async onPublish(): Promise<void> {
if (this.onPublishCallback) {
await this.onPublishCallback();
}
}
async refresh(): Promise<void> {
await this.onRefresh();
}
async publish(): Promise<void> {
await this.onPublish();
}
/**
* 全部页切图
*/
private async sliceAllPages(): Promise<void> {
if (!this.currentFile) {
new Notice('请先打开一个笔记');
return;
}
new Notice(`开始切图:共 ${this.pages.length}`);
try {
for (let i = 0; i < this.pages.length; i++) {
new Notice(`正在处理第 ${i + 1}/${this.pages.length} 页...`);
// 临时渲染这一页
this.currentPageIndex = i;
this.renderCurrentPage();
// 等待渲染完成
await new Promise(resolve => setTimeout(resolve, 200));
const pageElement = this.pageContainer.querySelector('.xhs-page') as HTMLElement;
if (pageElement) {
await sliceCurrentPage(pageElement, this.currentFile, i, this.app);
}
}
new Notice(`✅ 全部页切图完成:共 ${this.pages.length}`);
} catch (error) {
console.error('批量切图失败:', error);
new Notice('❌ 批量切图失败: ' + (error instanceof Error ? error.message : String(error)));
}
}
private async persistSettings(): Promise<void> {
try {
const plugin = (this.app as any)?.plugins?.getPlugin?.('note2any');
if (plugin?.saveSettings) {
await plugin.saveSettings();
}
} catch (error) {
console.warn('[XiaohongshuPreview] 保存设置失败', error);
}
}
/**
* 显示小红书预览视图
*/
show(): void {
if (this.container) {
this.container.style.display = 'flex';
}
// 确保平台选择器显示正确的选项
if (this.platformSelect) {
this.platformSelect.value = 'xiaohongshu';
}
}
/**
* 隐藏小红书预览视图
*/
hide(): void {
if (this.container) {
this.container.style.display = 'none';
}
}
/**
* 清理资源
*/
destroy(): void {
this.templateSelect = null as any;
this.previewWidthSelect = null as any;
this.fontSizeInput = null as any;
this.pageContainer = null as any;
this.pageNumberInput = null as any;
this.pageTotalLabel = null as any;
this.pages = [];
this.currentFile = null;
this.styleEl = null;
}
/** 组合并注入主题 + 高亮 + 自定义 CSS使用全局默认主题 */
private applyThemeCSS() {
if (!this.styleEl) return;
const themeName = this.settings.defaultStyle;
const highlightName = this.settings.defaultHighlight;
const theme = this.assetsManager.getTheme(themeName);
const highlight = this.assetsManager.getHighlight(highlightName);
const customCSS = (this.settings.useCustomCss || this.settings.customCSSNote.length>0) ? this.assetsManager.customCSS : '';
const baseCSS = this.settings.baseCSS ? `.note2any {${this.settings.baseCSS}}` : '';
const css = `${highlight?.css || ''}\n\n${theme?.css || ''}\n\n${baseCSS}\n\n${customCSS}`;
this.styleEl.textContent = css;
this.currentThemeClass = theme?.className || '';
}
private async repaginateAndRender(): Promise<void> {
if (!this.articleHTML) return;
const totalBefore = this.pages.length || 1;
const posRatio = (this.currentPageIndex + 0.5) / totalBefore; // 以当前页中心作为相对位置
//new Notice('重新分页中...');
const tempContainer = document.createElement('div');
tempContainer.innerHTML = this.articleHTML;
tempContainer.style.width = `${this.settings.sliceImageWidth}px`;
tempContainer.style.fontSize = `${this.currentFontSize}px`;
// 字体从全局主题中继承,无需手动指定
tempContainer.classList.add('note2any');
tempContainer.className = this.currentThemeClass ? `note2any ${this.currentThemeClass}` : 'note2any';
document.body.appendChild(tempContainer);
try {
this.pages = await paginateArticle(tempContainer, this.settings);
if (this.pages.length > 0) {
const newIndex = Math.floor(posRatio * this.pages.length - 0.5);
this.currentPageIndex = Math.min(this.pages.length - 1, Math.max(0, newIndex));
} else {
this.currentPageIndex = 0;
}
this.renderCurrentPage();
//new Notice(`重新分页完成:共 ${this.pages.length} 页`);
} catch (e) {
console.error('重新分页失败', e);
new Notice('重新分页失败');
} finally {
document.body.removeChild(tempContainer);
}
}
/**
* 更新主题和高亮选择器的值
*/
updateStyleAndHighlight(theme: string, highlight: string): void {
if (this.themeSelect) {
this.themeSelect.value = theme;
}
// 高亮已移除,保留此方法以兼容外部调用
this.applyThemeCSS();
}
}