update at 2025-10-09 21:19:57

This commit is contained in:
douboer
2025-10-09 21:19:57 +08:00
parent 002feedbe1
commit 1309caddc3
15 changed files with 1126 additions and 209 deletions

View File

@@ -9,7 +9,7 @@ import { UploadImageToWx } from './imagelib';
import { NMPSettings } from './settings';
import AssetsManager from './assets';
import InlineCSS from './inline-css';
import { wxGetToken, wxAddDraft, wxBatchGetMaterial, DraftArticle, DraftImageMediaId, DraftImages, wxAddDraftImages } from './weixin-api';
import { wxGetToken, wxAddDraft, wxBatchGetMaterial, DraftArticle, DraftImageMediaId, DraftImages, wxAddDraftImages } from './wechat/weixin-api';
import { MDRendererCallback } from './markdown/extension';
import { MarkedParser } from './markdown/parser';
import { LocalImageManager, LocalFile } from './markdown/local-file';
@@ -855,4 +855,4 @@ export class ArticleRender implements MDRendererCallback {
const key = category + ':' + id;
this.cachedElements.set(key, data);
}
}
}

View File

@@ -4,10 +4,10 @@
*/
import { getBlobArrayBuffer } from "obsidian";
import { wxUploadImage } from "./weixin-api";
import { wxUploadImage } from "./wechat/weixin-api";
import { NMPSettings } from "./settings";
import { IsWasmReady, LoadWasm } from "./wasm/wasm";
import AssetsManager from "./assets";
import AssetsManager from "./assets";
declare function GoWebpToJPG(data: Uint8Array): Uint8Array; // wasm 返回 Uint8Array
declare function GoWebpToPNG(data: Uint8Array): Uint8Array;
@@ -51,4 +51,4 @@ export async function UploadImageToWx(data: Blob, filename: string, token: strin
data = new Blob([bufferPart], { type: data.type });
}
return await wxUploadImage(data, filename, token, type);
}
}

View File

@@ -3,7 +3,7 @@
import { Tokens, MarkedExtension} from "marked";
import { Extension } from "./extension";
import AssetsManager from "src/assets";
import { wxWidget } from "src/weixin-api";
import { wxWidget } from 'src/wechat/weixin-api';
const icon_note = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-pencil"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"></path><path d="m15 5 4 4"></path></svg>`
const icon_abstract = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-clipboard-list"><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><path d="M12 11h4"></path><path d="M12 16h4"></path><path d="M8 11h.01"></path><path d="M8 16h.01"></path></svg>`

View File

@@ -7,7 +7,7 @@ import { MathRendererQueue } from "./math";
import { Extension } from "./extension";
import { UploadImageToWx } from "../imagelib";
import AssetsManager from "src/assets";
import { wxWidget } from "src/weixin-api";
import { wxWidget } from 'src/wechat/weixin-api';
export class CardDataManager {
private cardData: Map<string, string>;

View File

@@ -4,7 +4,7 @@ import { Tokens, MarkedExtension } from "marked";
import { Extension } from "./extension";
import AssetsManager from "src/assets";
import { ExpertSettings } from "src/expert-settings";
import { wxWidget } from "src/weixin-api";
import { wxWidget } from 'src/wechat/weixin-api';
export class HeadingRenderer extends Extension {
index = [0, 0, 0, 0];

View File

@@ -4,7 +4,7 @@ import { Tokens, MarkedExtension } from "marked";
import { Extension } from "./extension";
import { NMPSettings } from "src/settings";
import { uevent } from "src/utils";
import { wxWidget } from "src/weixin-api";
import { wxWidget } from 'src/wechat/weixin-api';
const widgetCache = new Map<string, string>();

View File

@@ -5,7 +5,7 @@
import { App, TextAreaComponent, PluginSettingTab, Setting, Notice, sanitizeHTMLToDom } from 'obsidian';
import NoteToMpPlugin from './main';
import { wxGetToken,wxEncrypt } from './weixin-api';
import { wxGetToken, wxEncrypt } from './wechat/weixin-api';
import { cleanMathCache } from './markdown/math';
import { NMPSettings } from './settings';
import { DocModal } from './doc-modal';

View File

@@ -9,7 +9,7 @@
* - 批量发布预设 / 图片处理 / 样式控制等选项
*/
import { wxKeyInfo } from './weixin-api';
import { wxKeyInfo } from './wechat/weixin-api';
export class NMPSettings {
defaultStyle: string;

View File

@@ -4,7 +4,7 @@
*/
import { App, sanitizeHTMLToDom, requestUrl, Platform } from "obsidian";
import * as postcss from "./postcss/postcss";
import * as postcss from "./postcss/postcss"; // 内置 PostCSS runtime解析主题 CSS 用于内联样式
let PluginVersion = "0.0.0";
let PlugPlatform = "obsidian";

View File

@@ -17,6 +17,8 @@ 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;
@@ -32,14 +34,11 @@ export class XiaohongshuPreview {
currentFile: TFile | null = null;
// UI 元素
topToolbar!: HTMLDivElement;
templateSelect!: HTMLSelectElement;
fontSizeInput!: HTMLInputElement;
previewWidthSelect!: HTMLSelectElement;
pageContainer!: HTMLDivElement;
bottomToolbar!: HTMLDivElement;
pageNavigation!: HTMLDivElement;
pageNumberInput!: HTMLInputElement;
pageTotalLabel!: HTMLSpanElement;
styleEl: HTMLStyleElement | null = null; // 主题样式注入节点
@@ -78,49 +77,28 @@ export class XiaohongshuPreview {
this.container.appendChild(this.styleEl);
}
// 顶部工具栏
this.buildTopToolbar();
// 页面容器
this.pageContainer = this.container.createDiv({ cls: 'xhs-page-container' });
// 分页导航
this.buildPageNavigation();
// 底部操作栏
this.buildBottomToolbar();
}
/**
* 构建顶部工具栏
*/
private buildTopToolbar(): void {
this.topToolbar = this.container.createDiv({ cls: 'xhs-top-toolbar' });
// 刷新按钮
const refreshBtn = this.topToolbar.createEl('button', { text: '🔄 刷新', cls: 'toolbar-button purple-gradient' });
refreshBtn.onclick = () => this.onRefresh();
// 发布按钮
const publishBtn = this.topToolbar.createEl('button', { text: '📤 发布', cls: 'toolbar-button' });
publishBtn.onclick = () => this.onPublish();
// 分隔线
const separator2 = this.topToolbar.createDiv({ cls: 'toolbar-separator' });
// 模板选择
const templateLabel = this.topToolbar.createDiv({ cls: 'toolbar-label' });
templateLabel.innerText = '模板';
this.templateSelect = this.topToolbar.createEl('select', { cls: 'xhs-select' });
const board = this.container.createDiv({ cls: 'xhs-board' });
const templateCard = this.createGridCard(board, 'xhs-area-template');
const templateLabel = templateCard.createDiv({ cls: 'xhs-label', text: '模板' });
this.templateSelect = templateCard.createEl('select', { cls: 'xhs-select' });
['默认模板', '简约模板', '杂志模板'].forEach(name => {
const option = this.templateSelect.createEl('option');
option.value = name;
option.text = name;
});
const previewWidthLabel = this.topToolbar.createDiv({ cls: 'toolbar-label' });
previewWidthLabel.innerText = '预览宽度';
this.previewWidthSelect = this.topToolbar.createEl('select', { cls: 'xhs-select' });
const refreshCard = this.createGridCard(board, 'xhs-area-refresh');
const refreshBtn = refreshCard.createEl('button', { text: '🔄 刷新', cls: 'toolbar-button purple-gradient' });
refreshBtn.onclick = () => this.onRefresh();
const publishCard = this.createGridCard(board, 'xhs-area-publish');
const publishBtn = publishCard.createEl('button', { text: '📤 发布', cls: 'toolbar-button' });
publishBtn.onclick = () => this.onPublish();
const previewCard = this.createGridCard(board, 'xhs-area-preview');
const previewLabel = previewCard.createDiv({ cls: 'xhs-label', text: '预览宽度' });
this.previewWidthSelect = previewCard.createEl('select', { cls: 'xhs-select' });
const currentPreviewWidth = this.settings.xhsPreviewWidth || XHS_PREVIEW_DEFAULT_WIDTH;
XHS_PREVIEW_WIDTH_OPTIONS.forEach(value => {
const option = this.previewWidthSelect.createEl('option');
@@ -141,12 +119,11 @@ export class XiaohongshuPreview {
this.previewWidthSelect.value = String(this.settings.xhsPreviewWidth || XHS_PREVIEW_DEFAULT_WIDTH);
}
};
// 字号控制(可直接编辑)
const fontSizeLabel = this.topToolbar.createDiv({ cls: 'toolbar-label' });
fontSizeLabel.innerText = '字号';
const fontSizeGroup = this.topToolbar.createDiv({ cls: 'font-size-group' });
const fontCard = this.createGridCard(board, 'xhs-area-font');
fontCard.createDiv({ cls: 'xhs-label', text: '字号' });
const fontSizeGroup = fontCard.createDiv({ cls: 'font-size-group' });
const decreaseBtn = fontSizeGroup.createEl('button', { text: '', cls: 'font-size-btn' });
decreaseBtn.onclick = () => this.changeFontSize(-1);
@@ -159,24 +136,19 @@ export class XiaohongshuPreview {
value: String(XHS_FONT_SIZE_DEFAULT)
}
});
this.fontSizeInput.style.width = '50px';
this.fontSizeInput.style.textAlign = 'center';
this.fontSizeInput.onchange = () => this.onFontSizeInputChanged();
const increaseBtn = fontSizeGroup.createEl('button', { text: '', cls: 'font-size-btn' });
increaseBtn.onclick = () => this.changeFontSize(1);
}
/**
* 构建分页导航
*/
private buildPageNavigation(): void {
this.pageNavigation = this.container.createDiv({ cls: 'xhs-page-navigation' });
const prevBtn = this.pageNavigation.createEl('button', { text: '', cls: 'xhs-nav-btn' });
const contentWrapper = board.createDiv({ cls: 'xhs-area-content' });
this.pageContainer = contentWrapper.createDiv({ cls: 'xhs-page-container' });
const paginationCard = this.createGridCard(board, 'xhs-area-pagination xhs-pagination');
const prevBtn = paginationCard.createEl('button', { text: '', cls: 'xhs-nav-btn' });
prevBtn.onclick = () => this.previousPage();
const indicator = this.pageNavigation.createDiv({ cls: 'xhs-page-indicator' });
const indicator = paginationCard.createDiv({ cls: 'xhs-page-indicator' });
this.pageNumberInput = indicator.createEl('input', {
cls: 'xhs-page-number-input',
attr: { type: 'text', value: '1', inputmode: 'numeric', 'aria-label': '当前页码' }
@@ -197,22 +169,20 @@ export class XiaohongshuPreview {
this.pageNumberInput.onblur = () => this.handlePageNumberInput();
this.pageTotalLabel = indicator.createEl('span', { cls: 'xhs-page-number-total', text: '/1' });
const nextBtn = this.pageNavigation.createEl('button', { text: '', cls: 'xhs-nav-btn' });
const nextBtn = paginationCard.createEl('button', { text: '', cls: 'xhs-nav-btn' });
nextBtn.onclick = () => this.nextPage();
const sliceCard = this.createGridCard(board, 'xhs-area-slice');
const sliceCurrentBtn = sliceCard.createEl('button', { text: '⬇ 当前页切图', cls: 'xhs-slice-btn' });
sliceCurrentBtn.onclick = () => this.sliceCurrentPage();
const sliceAllBtn = sliceCard.createEl('button', { text: '⇓ 全部页切图', cls: 'xhs-slice-btn secondary' });
sliceAllBtn.onclick = () => this.sliceAllPages();
}
/**
* 构建底部操作栏
*/
private buildBottomToolbar(): void {
this.bottomToolbar = this.container.createDiv({ cls: 'xhs-bottom-toolbar' });
const currentPageBtn = this.bottomToolbar.createEl('button', { text: '⬇ 当前页切图', cls: 'xhs-slice-btn' });
currentPageBtn.onclick = () => this.sliceCurrentPage();
const allPagesBtn = this.bottomToolbar.createEl('button', { text: '⇓ 全部页切图', cls: 'xhs-slice-btn secondary' });
allPagesBtn.onclick = () => this.sliceAllPages();
private createGridCard(parent: HTMLElement, areaClass: string): HTMLDivElement {
return parent.createDiv({ cls: `xhs-card ${areaClass}` });
}
/**
@@ -336,6 +306,7 @@ export class XiaohongshuPreview {
pageElement.style.width = `${actualWidth}px`;
pageElement.style.height = `${actualHeight}px`;
pageElement.style.transform = `scale(${scale})`;
pageElement.style.transformOrigin = 'top left';
pageElement.style.position = 'absolute';
pageElement.style.top = '0';
pageElement.style.left = '0';
@@ -521,13 +492,10 @@ export class XiaohongshuPreview {
* 清理资源
*/
destroy(): void {
this.topToolbar = null as any;
this.templateSelect = null as any;
this.previewWidthSelect = null as any;
this.fontSizeInput = null as any;
this.pageContainer = null as any;
this.bottomToolbar = null as any;
this.pageNavigation = null as any;
this.pageNumberInput = null as any;
this.pageTotalLabel = null as any;
this.pages = [];