/** * 文件:settings.ts * 作用:插件全局设置模型(单例)与序列化/反序列化逻辑。 * 内容: * - 默认值初始化 * - loadSettings: 反序列化存储数据并兼容旧字段 * - allSettings: 统一导出用于持久化 * - 会员 / 授权信息校验(isAuthKeyVaild) * - 批量发布预设 / 图片处理 / 样式控制等选项 */ import { wxKeyInfo } from './wechat/weixin-api'; export class NMPSettings { defaultStyle: string; defaultHighlight: string; showStyleUI: boolean; linkStyle: string; embedStyle: string; lineNumber: boolean; authKey: string; useCustomCss: boolean; customCSSNote: string; expertSettingsNote: string; wxInfo: {name:string, appid:string, secret:string}[]; math: string; expireat: Date | null = null; isVip: boolean = false; baseCSS: string; watermark: string; useFigcaption: boolean; excalidrawToPNG: boolean; isLoaded: boolean = false; enableEmptyLine: boolean = false; needOpenComment: boolean = true; enableMarkdownImageToWikilink: boolean = true; // 自动将 ![alt](path/file.ext) 转为 ![[file.ext]] // gallery 相关配置:根目录前缀 & 选取图片数量 galleryPrePath: string; galleryNumPic: number; // 无图片时的默认封面(wikilink 或 URL 均可) defaultCoverPic: string; // 是否忽略 frontmatter 中的 cover/image 字段(用户要求:封面不使用 frontmatter image) ignoreFrontmatterImage: boolean; // 批量发布筛选条件预设 batchPublishPresets: Array<{ name: string; tags?: string[]; folders?: string[]; filenameKeywords?: string[]; }>; // 切图相关配置 sliceImageSavePath: string; // 切图保存路径 sliceImageWidth: number; // 切图宽度(像素) sliceImageAspectRatio: string; // 横竖比例,格式 "3:4" xhsPreviewWidth: number; // 小红书预览宽度(像素) private static instance: NMPSettings; // 静态方法,用于获取实例 public static getInstance(): NMPSettings { if (!NMPSettings.instance) { NMPSettings.instance = new NMPSettings(); } return NMPSettings.instance; } private constructor() { this.defaultStyle = 'obsidian-light'; this.defaultHighlight = '默认'; this.showStyleUI = true; this.linkStyle = 'inline'; this.embedStyle = 'content'; this.lineNumber = true; this.useCustomCss = false; this.authKey = ''; this.wxInfo = []; this.math = 'latex'; this.baseCSS = ''; this.watermark = ''; this.useFigcaption = false; this.customCSSNote = ''; this.excalidrawToPNG = false; this.expertSettingsNote = ''; this.enableEmptyLine = false; this.needOpenComment = true; this.enableMarkdownImageToWikilink = true; // 默认值:用户原先硬编码路径 & 前 2 张 this.galleryPrePath = '/Users/gavin/myweb/static'; this.galleryNumPic = 2; // 默认封面:使用当前笔记同目录下的 cover.png (若存在会被后续流程正常解析;不存在则无效但可被用户覆盖) this.defaultCoverPic = 'cover.png'; this.ignoreFrontmatterImage = false; // 批量发布预设 this.batchPublishPresets = [ { name: '篆刻文章', tags: ['篆刻'], folders: [], filenameKeywords: [] } ]; // 切图配置默认值(使用 vault 相对路径) this.sliceImageSavePath = 'xhs-images'; this.sliceImageWidth = 1080; this.sliceImageAspectRatio = '3:4'; this.xhsPreviewWidth = 540; } resetStyelAndHighlight() { this.defaultStyle = 'obsidian-light'; this.defaultHighlight = '默认'; } public static loadSettings(data: any) { if (!data) return; const { defaultStyle, defaultHighlight, showStyleUI, linkStyle, embedStyle, lineNumber, authKey, wxInfo, math, useCustomCss, baseCSS, watermark, useFigcaption, customCSSNote, excalidrawToPNG, expertSettingsNote, ignoreEmptyLine, enableMarkdownImageToWikilink, needOpenComment, galleryPrePath, galleryNumPic, defaultCoverPic, ignoreFrontmatterImage, batchPublishPresets = [], sliceImageSavePath, sliceImageWidth, sliceImageAspectRatio, xhsPreviewWidth } = data; const settings = NMPSettings.getInstance(); if (defaultStyle) settings.defaultStyle = defaultStyle; if (defaultHighlight) settings.defaultHighlight = defaultHighlight; if (showStyleUI !== undefined) settings.showStyleUI = showStyleUI; if (linkStyle) settings.linkStyle = linkStyle; if (embedStyle) settings.embedStyle = embedStyle; if (lineNumber !== undefined) settings.lineNumber = lineNumber; if (authKey) settings.authKey = authKey; if (wxInfo) settings.wxInfo = wxInfo; if (math) settings.math = math; if (useCustomCss !== undefined) settings.useCustomCss = useCustomCss; if (baseCSS) settings.baseCSS = baseCSS; if (watermark) settings.watermark = watermark; if (useFigcaption !== undefined) settings.useFigcaption = useFigcaption; if (customCSSNote) settings.customCSSNote = customCSSNote; if (excalidrawToPNG !== undefined) settings.excalidrawToPNG = excalidrawToPNG; if (expertSettingsNote) settings.expertSettingsNote = expertSettingsNote; if (ignoreEmptyLine !== undefined) settings.enableEmptyLine = !!ignoreEmptyLine; if (needOpenComment !== undefined) settings.needOpenComment = !!needOpenComment; if (enableMarkdownImageToWikilink !== undefined) settings.enableMarkdownImageToWikilink = !!enableMarkdownImageToWikilink; if (galleryPrePath) settings.galleryPrePath = galleryPrePath; if (galleryNumPic !== undefined && Number.isFinite(galleryNumPic)) settings.galleryNumPic = Math.max(1, parseInt(galleryNumPic)); if (defaultCoverPic !== undefined) settings.defaultCoverPic = String(defaultCoverPic).trim(); if (ignoreFrontmatterImage !== undefined) settings.ignoreFrontmatterImage = !!ignoreFrontmatterImage; if (Array.isArray(batchPublishPresets)) settings.batchPublishPresets = batchPublishPresets; if (sliceImageSavePath) settings.sliceImageSavePath = sliceImageSavePath; if (sliceImageWidth !== undefined && Number.isFinite(sliceImageWidth)) settings.sliceImageWidth = Math.max(100, parseInt(sliceImageWidth)); if (sliceImageAspectRatio) settings.sliceImageAspectRatio = sliceImageAspectRatio; if (xhsPreviewWidth !== undefined && Number.isFinite(xhsPreviewWidth)) { settings.xhsPreviewWidth = Math.max(100, parseInt(xhsPreviewWidth)); } settings.getExpiredDate(); settings.isLoaded = true; } public static allSettings() { const settings = NMPSettings.getInstance(); return { 'defaultStyle': settings.defaultStyle, 'defaultHighlight': settings.defaultHighlight, 'showStyleUI': settings.showStyleUI, 'linkStyle': settings.linkStyle, 'embedStyle': settings.embedStyle, 'lineNumber': settings.lineNumber, 'authKey': settings.authKey, 'wxInfo': settings.wxInfo, 'math': settings.math, 'useCustomCss': settings.useCustomCss, 'baseCSS': settings.baseCSS, 'watermark': settings.watermark, 'useFigcaption': settings.useFigcaption, 'customCSSNote': settings.customCSSNote, 'excalidrawToPNG': settings.excalidrawToPNG, 'expertSettingsNote': settings.expertSettingsNote, 'ignoreEmptyLine': settings.enableEmptyLine, 'needOpenComment': settings.needOpenComment, 'enableMarkdownImageToWikilink': settings.enableMarkdownImageToWikilink, 'galleryPrePath': settings.galleryPrePath, 'galleryNumPic': settings.galleryNumPic, 'defaultCoverPic': settings.defaultCoverPic, 'ignoreFrontmatterImage': settings.ignoreFrontmatterImage, 'batchPublishPresets': settings.batchPublishPresets, 'sliceImageSavePath': settings.sliceImageSavePath, 'sliceImageWidth': settings.sliceImageWidth, 'sliceImageAspectRatio': settings.sliceImageAspectRatio, 'xhsPreviewWidth': settings.xhsPreviewWidth, } } getExpiredDate() { if (this.authKey.length == 0) return; wxKeyInfo(this.authKey).then((res) => { if (res.status == 200) { if (res.json.vip) { this.isVip = true; } this.expireat = new Date(res.json.expireat); } }) } isAuthKeyVaild() { if (this.authKey.length == 0) return false; if (this.isVip) return true; if (this.expireat == null) return false; return this.expireat > new Date(); } }