From 0ab20de8802a6e356f3552520d869f782f863e07 Mon Sep 17 00:00:00 2001 From: douboer Date: Fri, 10 Oct 2025 21:54:05 +0800 Subject: [PATCH] update at 2025-10-10 21:54:05 --- docs/CHANGELOG.md | 2 +- docs/mp_todolist.md | 12 +- src/article-render.ts | 9 +- src/imagelib.ts | 23 +- src/platform-chooser.ts | 2 +- src/setting-tab.ts | 467 ++++++++++++++++++++++------------------ src/settings.ts | 5 + styles.css | 51 ++++- todolist.md | 4 +- 9 files changed, 347 insertions(+), 228 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 867838d..33dcbaa 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -12,6 +12,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve ### Changed - README:新增图片方向处理说明、Gallery 参数使用示例。 +- 预览布局:微信与小红书界面改为统一的网格化布局(`wechat-board` / `xhs-board`),组件按区域栅格排列,便于维护与对齐。 ### Notes - 若遇到其他 EXIF 方向值(除 1/3/6/8),当前保持原样,可后续扩展。 @@ -35,4 +36,3 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve - 发布新版本:更新 `package.json` / `manifest.json` 的版本号;追加 `versions.json`;将当前 Unreleased 条目移动为新的版本号,并添加日期;再创建新的 Unreleased 模板。 - 提交信息建议:`feat: ...`, `fix: ...`, `docs: ...`, `refactor: ...` 等 Conventional Commits 风格。 - diff --git a/docs/mp_todolist.md b/docs/mp_todolist.md index 6a798d5..3a989be 100644 --- a/docs/mp_todolist.md +++ b/docs/mp_todolist.md @@ -21,7 +21,7 @@ 如n=2,取出的图片为xx.jpg,yy.png,那么把{{}}{{}}替换为: ![[xx.jpg]] ![[yy.png]] -✅ + ✅ 3. 对如下: @@ -46,7 +46,7 @@ src可能使用link: ✅ 4. -参考以下代码,渲染[fig content/],|| content,||r content,||g content,||b content等标签: + 参考以下代码,渲染[fig content/],|| content,||r content,||g content,||b content等标签: `\[fig([^>]*?)/\]` `$1` `\|\| (.*)` `

$1

` `\|\|r (.*)` `

$1

` @@ -105,7 +105,9 @@ views: ``` ✅ -10. 默认选择“原创”“允许留言”。 +10. 默认选择 + - “允许留言” ✅ + - “原创” 11. gallery短代码增加是否使用dir中的所有图片的开关。mppickall=1,选取dir中的所有图片,mppickall=0,按“选取图片数”配置选取图片数量。 {{}}{{}} @@ -136,3 +138,7 @@ Orientation : 6 -- 图片左旋90度,需右选90才正常。 为了规避这个问题,图片不做旋转处理,直接转为png上传公众号。解决。因为PNG不带orientation信息。 ✅ +- 封面图片没有正确旋转。 +公众号上传仍然有图片旋转问题,同样需要取png图片避免旋转问题 +✅ + diff --git a/src/article-render.ts b/src/article-render.ts index c66408c..c9f7c2d 100644 --- a/src/article-render.ts +++ b/src/article-render.ts @@ -381,7 +381,9 @@ export class ArticleRender implements MDRendererCallback { res.cover = undefined; // 忽略 frontmatter } res.thumb_media_id = this.getFrontmatterValue(frontmatter, keys.thumb_media_id); - res.need_open_comment = frontmatter[keys.need_open_comment] ? 1 : undefined; + if (frontmatter[keys.need_open_comment] !== undefined) { + res.need_open_comment = frontmatter[keys.need_open_comment] ? 1 : 0; + } res.only_fans_can_comment = frontmatter[keys.only_fans_can_comment] ? 1 : undefined; res.appid = this.getFrontmatterValue(frontmatter, keys.appid); if (res.appid && !res.appid.startsWith('wx')) { @@ -478,6 +480,9 @@ export class ArticleRender implements MDRendererCallback { } } } + if (res.need_open_comment === undefined) { + res.need_open_comment = this.settings.needOpenComment ? 1 : 0; + } return res; } @@ -717,7 +722,7 @@ export class ArticleRender implements MDRendererCallback { article_type: 'newspic', title: metadata.title || this.title, content: content, - need_open_commnet: metadata.need_open_comment || 0, + need_open_commnet: metadata.need_open_comment ?? 0, only_fans_can_comment: metadata.only_fans_can_comment || 0, image_info: { image_list: imageList, diff --git a/src/imagelib.ts b/src/imagelib.ts index 9452d6b..b80aeca 100644 --- a/src/imagelib.ts +++ b/src/imagelib.ts @@ -8,6 +8,7 @@ import { wxUploadImage } from "./wechat/weixin-api"; import { NMPSettings } from "./settings"; import { IsWasmReady, LoadWasm } from "./wasm/wasm"; import AssetsManager from "./assets"; +import { convertJpegIfNeeded } from "./exif-orientation"; declare function GoWebpToJPG(data: Uint8Array): Uint8Array; // wasm 返回 Uint8Array declare function GoWebpToPNG(data: Uint8Array): Uint8Array; @@ -37,6 +38,18 @@ export async function UploadImageToWx(data: Blob, filename: string, token: strin if (!IsImageLibReady()) { await PrepareImageLib(); } + + try { + // 公众号端仍然存在基于 EXIF 的旋转问题: + // 统一将待上传的 JPEG 转为 PNG 并忽略 Orientation,避免出现倒置/倾斜。 + const converted = await convertJpegIfNeeded(data, filename); + if (converted.changed) { + data = converted.blob; + filename = converted.filename; + } + } catch (error) { + console.warn('[UploadImageToWx] convert to PNG failed, fallback to original', error); + } const watermark = NMPSettings.getInstance().watermark; if (watermark != null && watermark != '') { @@ -44,11 +57,11 @@ export async function UploadImageToWx(data: Blob, filename: string, token: strin if (watermarkData == null) { throw new Error('水印图片不存在: ' + watermark); } - const watermarkImg = AddWatermark(await data.arrayBuffer(), watermarkData); - // AddWatermark 返回 Uint8Array,Blob 的类型签名对某些 TS 配置可能对 ArrayBufferLike 有严格区分 - // 此处使用其底层 ArrayBuffer 来构造 Blob,避免类型不兼容错误 - const bufferPart = watermarkImg.buffer as ArrayBuffer; - data = new Blob([bufferPart], { type: data.type }); + const watermarkImg = AddWatermark(await data.arrayBuffer(), watermarkData); + // AddWatermark 返回 Uint8Array,Blob 的类型签名对某些 TS 配置可能对 ArrayBufferLike 有严格区分 + // 此处使用其底层 ArrayBuffer 来构造 Blob,避免类型不兼容错误 + const bufferPart = watermarkImg.buffer as ArrayBuffer; + data = new Blob([bufferPart], { type: data.type }); } return await wxUploadImage(data, filename, token, type); } diff --git a/src/platform-chooser.ts b/src/platform-chooser.ts index 569b200..be1e40e 100644 --- a/src/platform-chooser.ts +++ b/src/platform-chooser.ts @@ -43,7 +43,7 @@ interface PlatformInfo { * 支持的平台列表 */ const SUPPORTED_PLATFORMS: PlatformInfo[] = [ - { value: 'wechat', label: '微信公众号', icon: '📱' }, + { value: 'wechat', label: '公众号', icon: '📱' }, { value: 'xiaohongshu', label: '小红书', icon: '📔' } ]; diff --git a/src/setting-tab.ts b/src/setting-tab.ts index f4cbd00..f84dc65 100644 --- a/src/setting-tab.ts +++ b/src/setting-tab.ts @@ -160,99 +160,141 @@ export class NoteToMpSettingTab extends PluginSettingTab { this.wxInfo = this.parseWXInfo(); - const helpEl = containerEl.createEl('div', { cls: 'setting-help-section' }); - helpEl.createEl('h2', {text: '帮助文档', cls: 'setting-help-title'}); - helpEl.createEl('a', {text: 'https://sunboshi.tech/doc', attr: {href: 'https://sunboshi.tech/doc'}}); + const tabs = [ + { id: 'style', label: '样式', render: (panel: HTMLElement) => this.renderStyleTab(panel) }, + { id: 'shortcode', label: '短代码', render: (panel: HTMLElement) => this.renderShortcodeTab(panel) }, + { id: 'theme', label: '主题', render: (panel: HTMLElement) => this.renderThemeTab(panel) }, + { id: 'image', label: '图片', render: (panel: HTMLElement) => this.renderImageTab(panel) }, + { id: 'user', label: '用户信息', render: (panel: HTMLElement) => this.renderUserTab(panel) }, + ]; - containerEl.createEl('h2', {text: '插件设置'}); + const tabBar = containerEl.createDiv({ cls: 'nmp-settings-tabs' }); + const panelsWrapper = containerEl.createDiv({ cls: 'nmp-settings-panels' }); - new Setting(containerEl) + const panelMap = new Map(); + const buttonMap = new Map(); + + const activate = (id: string) => { + buttonMap.forEach((btn, key) => btn.toggleClass('is-active', key === id)); + panelMap.forEach((panel, key) => panel.toggleClass('is-active', key === id)); + }; + + tabs.forEach((tab) => { + const button = tabBar.createEl('button', { text: tab.label, cls: 'nmp-settings-tab-button' }); + button.onclick = () => activate(tab.id); + buttonMap.set(tab.id, button); + + const panel = panelsWrapper.createDiv({ cls: 'nmp-settings-panel', attr: { 'data-tab': tab.id } }); + panelMap.set(tab.id, panel); + tab.render(panel); + }); + + if (tabs.length > 0) { + activate(tabs[0].id); + } + } + + private renderStyleTab(panel: HTMLElement): void { + + new Setting(panel) .setName('默认样式') .addDropdown(dropdown => { - const styles = this.plugin.assetsManager.themes; - for (let s of styles) { - dropdown.addOption(s.className, s.name); - } + const styles = this.plugin.assetsManager.themes; + for (const s of styles) { + dropdown.addOption(s.className, s.name); + } dropdown.setValue(this.settings.defaultStyle); - dropdown.onChange(async (value) => { + dropdown.onChange(async (value) => { this.settings.defaultStyle = value; await this.plugin.saveSettings(); - }); - }); - - new Setting(containerEl) - .setName('代码高亮') - .addDropdown(dropdown => { - const styles = this.plugin.assetsManager.highlights; - for (let s of styles) { - dropdown.addOption(s.name, s.name); - } - dropdown.setValue(this.settings.defaultHighlight); - dropdown.onChange(async (value) => { - this.settings.defaultHighlight = value; - await this.plugin.saveSettings(); - }); - }); - - new Setting(containerEl) - .setName('在工具栏展示样式选择') - .setDesc('建议在移动端关闭,可以增大文章预览区域') - .addToggle(toggle => { - toggle.setValue(this.settings.showStyleUI); - toggle.onChange(async (value) => { - this.settings.showStyleUI = value; - await this.plugin.saveSettings(); }); }); - new Setting(containerEl) + new Setting(panel) + .setName('代码高亮') + .addDropdown(dropdown => { + const styles = this.plugin.assetsManager.highlights; + for (const s of styles) { + dropdown.addOption(s.name, s.name); + } + dropdown.setValue(this.settings.defaultHighlight); + dropdown.onChange(async (value) => { + this.settings.defaultHighlight = value; + await this.plugin.saveSettings(); + }); + }); + + new Setting(panel) .setName('链接展示样式') .addDropdown(dropdown => { dropdown.addOption('inline', '内嵌'); - dropdown.addOption('footnote', '脚注'); + dropdown.addOption('footnote', '脚注'); dropdown.setValue(this.settings.linkStyle); dropdown.onChange(async (value) => { - this.settings.linkStyle = value; + this.settings.linkStyle = value; await this.plugin.saveSettings(); }); }); - new Setting(containerEl) + new Setting(panel) .setName('文件嵌入展示样式') .addDropdown(dropdown => { dropdown.addOption('quote', '引用'); - dropdown.addOption('content', '正文'); + dropdown.addOption('content', '正文'); dropdown.setValue(this.settings.embedStyle); dropdown.onChange(async (value) => { - this.settings.embedStyle = value; + this.settings.embedStyle = value; await this.plugin.saveSettings(); }); }); - new Setting(containerEl) + new Setting(panel) .setName('数学公式语法') .addDropdown(dropdown => { dropdown.addOption('latex', 'latex'); - dropdown.addOption('asciimath', 'asciimath'); + dropdown.addOption('asciimath', 'asciimath'); dropdown.setValue(this.settings.math); dropdown.onChange(async (value) => { - this.settings.math = value; + this.settings.math = value; cleanMathCache(); await this.plugin.saveSettings(); }); }); - new Setting(containerEl) + new Setting(panel) .setName('显示代码行号') .addToggle(toggle => { - toggle.setValue(this.settings.lineNumber); + toggle.setValue(this.settings.lineNumber); toggle.onChange(async (value) => { - this.settings.lineNumber = value; + this.settings.lineNumber = value; await this.plugin.saveSettings(); }); - }) + }); - new Setting(containerEl) + new Setting(panel) + .setName('启用空行渲染') + .addToggle(toggle => { + toggle.setValue(this.settings.enableEmptyLine); + toggle.onChange(async (value) => { + this.settings.enableEmptyLine = value; + await this.plugin.saveSettings(); + }); + }); + + new Setting(panel) + .setName('默认开启评论') + .setDesc('发布到公众号时默认开启评论,可在 frontmatter 使用 need_open_comment 关闭') + .addToggle(toggle => { + toggle.setValue(this.settings.needOpenComment); + toggle.onChange(async (value) => { + this.settings.needOpenComment = value; + await this.plugin.saveSettings(); + }); + }); + } + + private renderShortcodeTab(panel: HTMLElement): void { + new Setting(panel) .setName('Gallery 根路径') .setDesc('用于 {{}} 短代码解析;需指向本地图片根目录') .addText(text => { @@ -265,7 +307,7 @@ export class NoteToMpSettingTab extends PluginSettingTab { text.inputEl.setAttr('style', 'width: 360px;'); }); - new Setting(containerEl) + new Setting(panel) .setName('Gallery 选取图片数') .setDesc('每个 gallery 短代码最多替换为前 N 张图片') .addText(text => { @@ -281,7 +323,18 @@ export class NoteToMpSettingTab extends PluginSettingTab { text.inputEl.setAttr('style', 'width: 120px;'); }); - new Setting(containerEl) + new Setting(panel) + .setName('忽略 frontmatter 封面') + .setDesc('开启后不使用 frontmatter 中 cover/image 字段,封面将按正文首图→gallery→默认封面回退') + .addToggle(toggle => { + toggle.setValue(this.settings.ignoreFrontmatterImage); + toggle.onChange(async (value) => { + this.settings.ignoreFrontmatterImage = value; + await this.plugin.saveSettings(); + }); + }); + + new Setting(panel) .setName('默认封面图片') .setDesc('当文章无任何图片/短代码时使用;可填 wikilink 文件名或 http(s) URL') .addText(text => { @@ -293,33 +346,84 @@ export class NoteToMpSettingTab extends PluginSettingTab { }); text.inputEl.setAttr('style', 'width: 360px;'); }); + } - new Setting(containerEl) - .setName('忽略 frontmatter 封面') - .setDesc('开启后不使用 frontmatter 中 cover/image 字段,封面将按正文首图→gallery→默认封面回退') - .addToggle(toggle => { - toggle.setValue(this.settings.ignoreFrontmatterImage); - toggle.onChange(async (value) => { - this.settings.ignoreFrontmatterImage = value; + private renderThemeTab(panel: HTMLElement): void { + new Setting(panel) + .setName('获取更多主题') + .addButton(button => { + button.setButtonText('下载'); + button.onClick(async () => { + button.setButtonText('下载中...'); + await this.plugin.assetsManager.downloadThemes(); + button.setButtonText('下载完成'); + }); + }) + .addButton(button => { + button.setIcon('folder-open'); + button.onClick(async () => { + await this.plugin.assetsManager.openAssets(); + }); + }); + + new Setting(panel) + .setName('清空主题') + .addButton(button => { + button.setButtonText('清空'); + button.onClick(async () => { + await this.plugin.assetsManager.removeThemes(); + this.settings.resetStyelAndHighlight(); await this.plugin.saveSettings(); }); }); + new Setting(panel) + .setName('全局CSS属性') + .setDesc('只能填写CSS属性,不能写选择器') + .addTextArea(text => { + this.wxTextArea = text; + text.setPlaceholder('请输入CSS属性,如:background: #fff;padding: 10px;') + .setValue(this.settings.baseCSS) + .onChange(async (value) => { + this.settings.baseCSS = value; + await this.plugin.saveSettings(); + }) + .inputEl.setAttr('style', 'width: 520px; height: 60px;'); + }); - new Setting(containerEl) - .setName('启用空行渲染') - .addToggle(toggle => { - toggle.setValue(this.settings.enableEmptyLine); - toggle.onChange(async (value) => { - this.settings.enableEmptyLine = value; - await this.plugin.saveSettings(); - }); - }) + const customCSSDoc = '使用指南:https://sunboshi.tech/customcss'; + new Setting(panel) + .setName('自定义CSS笔记') + .setDesc(sanitizeHTMLToDom(customCSSDoc)) + .addText(text => { + text.setPlaceholder('请输入自定义CSS笔记标题') + .setValue(this.settings.customCSSNote) + .onChange(async (value) => { + this.settings.customCSSNote = value.trim(); + await this.plugin.saveSettings(); + await this.plugin.assetsManager.loadCustomCSS(); + }) + .inputEl.setAttr('style', 'width: 320px;'); + }); - // 切图配置区块 - containerEl.createEl('h2', {text: '切图配置'}); + const expertDoc = '使用指南:https://sunboshi.tech/expert'; + new Setting(panel) + .setName('专家设置笔记') + .setDesc(sanitizeHTMLToDom(expertDoc)) + .addText(text => { + text.setPlaceholder('请输入专家设置笔记标题') + .setValue(this.settings.expertSettingsNote) + .onChange(async (value) => { + this.settings.expertSettingsNote = value.trim(); + await this.plugin.saveSettings(); + await this.plugin.assetsManager.loadExpertSettings(); + }) + .inputEl.setAttr('style', 'width: 320px;'); + }); + } - new Setting(containerEl) + private renderImageTab(panel: HTMLElement): void { + new Setting(panel) .setName('切图保存路径') .setDesc('切图文件的保存目录,默认:/Users/gavin/note2mp/images/xhs') .addText(text => { @@ -332,7 +436,7 @@ export class NoteToMpSettingTab extends PluginSettingTab { text.inputEl.setAttr('style', 'width: 360px;'); }); - new Setting(containerEl) + new Setting(panel) .setName('切图宽度') .setDesc('长图及切图的宽度(像素),默认:1080') .addText(text => { @@ -348,7 +452,7 @@ export class NoteToMpSettingTab extends PluginSettingTab { text.inputEl.setAttr('style', 'width: 120px;'); }); - new Setting(containerEl) + new Setting(panel) .setName('切图横竖比例') .setDesc('格式:宽:高,例如 3:4 表示竖图,16:9 表示横图') .addText(text => { @@ -360,182 +464,119 @@ export class NoteToMpSettingTab extends PluginSettingTab { }); text.inputEl.setAttr('style', 'width: 120px;'); }); - - new Setting(containerEl) - .setName('渲染图片标题') - .addToggle(toggle => { - toggle.setValue(this.settings.useFigcaption); - toggle.onChange(async (value) => { - this.settings.useFigcaption = value; - await this.plugin.saveSettings(); - }); - }) - new Setting(containerEl) - .setName('Excalidraw 渲染为 PNG 图片') - .setDesc('开启:将 Excalidraw 笔记/嵌入转换为位图 PNG 插入;关闭:保持原始 SVG/矢量渲染(更清晰,体积更小)。') - .addToggle(toggle => { - toggle.setValue(this.settings.excalidrawToPNG); - toggle.onChange(async (value) => { - this.settings.excalidrawToPNG = value; - await this.plugin.saveSettings(); + new Setting(panel) + .setName('渲染图片标题') + .addToggle(toggle => { + toggle.setValue(this.settings.useFigcaption); + toggle.onChange(async (value) => { + this.settings.useFigcaption = value; + await this.plugin.saveSettings(); + }); }); - }) - new Setting(containerEl) + new Setting(panel) + .setName('Excalidraw 渲染为 PNG 图片') + .setDesc('开启:将 Excalidraw 笔记/嵌入转换为位图 PNG 插入;关闭:保持原始 SVG/矢量渲染。') + .addToggle(toggle => { + toggle.setValue(this.settings.excalidrawToPNG); + toggle.onChange(async (value) => { + this.settings.excalidrawToPNG = value; + await this.plugin.saveSettings(); + }); + }); + + new Setting(panel) .setName('水印图片') + .setDesc('可填写文件名或 URL') .addText(text => { - text.setPlaceholder('请输入图片名称') + text.setPlaceholder('例如 watermark.png') .setValue(this.settings.watermark) .onChange(async (value) => { - this.settings.watermark = value.trim(); + this.settings.watermark = value.trim(); await this.plugin.saveSettings(); }) - .inputEl.setAttr('style', 'width: 320px;') - }) - - new Setting(containerEl) - .setName('获取更多主题') - .addButton(button => { - button.setButtonText('下载'); - button.onClick(async () => { - button.setButtonText('下载中...'); - await this.plugin.assetsManager.downloadThemes(); - button.setButtonText('下载完成'); - }); - }) - .addButton(button => { - button.setIcon('folder-open'); - button.onClick(async () => { - await this.plugin.assetsManager.openAssets(); - }); + .inputEl.setAttr('style', 'width: 320px;'); }); + } - new Setting(containerEl) - .setName('清空主题') - .addButton(button => { - button.setButtonText('清空'); - button.onClick(async () => { - await this.plugin.assetsManager.removeThemes(); - this.settings.resetStyelAndHighlight(); - await this.plugin.saveSettings(); - }); - }) - new Setting(containerEl) - .setName('全局CSS属性') - .setDesc('只能填写CSS属性,不能写选择器') - .addTextArea(text => { - this.wxTextArea = text; - text.setPlaceholder('请输入CSS属性,如:background: #fff;padding: 10px;') - .setValue(this.settings.baseCSS) - .onChange(async (value) => { - this.settings.baseCSS = value; - await this.plugin.saveSettings(); - }) - .inputEl.setAttr('style', 'width: 520px; height: 60px;'); - }) - const customCSSDoc = '使用指南:https://sunboshi.tech/customcss'; - new Setting(containerEl) - .setName('自定义CSS笔记') - .setDesc(sanitizeHTMLToDom(customCSSDoc)) - .addText(text => { - text.setPlaceholder('请输入自定义CSS笔记标题') - .setValue(this.settings.customCSSNote) - .onChange(async (value) => { - this.settings.customCSSNote = value.trim(); - await this.plugin.saveSettings(); - await this.plugin.assetsManager.loadCustomCSS(); - }) - .inputEl.setAttr('style', 'width: 320px;') - }); - - const expertDoc = '使用指南:https://sunboshi.tech/expert'; - new Setting(containerEl) - .setName('专家设置笔记') - .setDesc(sanitizeHTMLToDom(expertDoc)) - .addText(text => { - text.setPlaceholder('请输入专家设置笔记标题') - .setValue(this.settings.expertSettingsNote) - .onChange(async (value) => { - this.settings.expertSettingsNote = value.trim(); - await this.plugin.saveSettings(); - await this.plugin.assetsManager.loadExpertSettings(); - }) - .inputEl.setAttr('style', 'width: 320px;') - }); - + private renderUserTab(panel: HTMLElement): void { let descHtml = '详情说明:https://sunboshi.tech/subscribe'; if (this.settings.isVip) { descHtml = '👑永久会员
' + descHtml; } else if (this.settings.expireat) { const timestr = this.settings.expireat.toLocaleString(); - descHtml = `有效期至:${timestr}
${descHtml}` + descHtml = `有效期至:${timestr}
${descHtml}`; } - new Setting(containerEl) + + new Setting(panel) .setName('注册码(AuthKey)') .setDesc(sanitizeHTMLToDom(descHtml)) .addText(text => { text.setPlaceholder('请输入注册码') - .setValue(this.settings.authKey) - .onChange(async (value) => { + .setValue(this.settings.authKey) + .onChange(async (value) => { this.settings.authKey = value.trim(); - this.settings.getExpiredDate(); - await this.plugin.saveSettings(); - }) - .inputEl.setAttr('style', 'width: 320px;') - }).descEl.setAttr('style', '-webkit-user-select: text; user-select: text;') - - + this.settings.getExpiredDate(); + await this.plugin.saveSettings(); + }) + .inputEl.setAttr('style', 'width: 320px;'); + }).descEl.setAttr('style', '-webkit-user-select: text; user-select: text;'); + let isClear = this.settings.wxInfo.length > 0; let isRealClear = false; const buttonText = isClear ? '清空公众号信息' : '保存公众号信息'; - new Setting(containerEl) + + new Setting(panel) .setName('公众号信息') .addTextArea(text => { this.wxTextArea = text; - text.setPlaceholder('请输入公众号信息\n格式:公众号名称|公众号AppID|公众号AppSecret\n多个公众号请换行输入\n输入完成后点击加密按钮') - .setValue(this.wxInfo) + text.setPlaceholder('请输入公众号信息\n格式:公众号名称|公众号AppID|公众号AppSecret\n多个公众号请换行输入\n输入完成后点击加密按钮') + .setValue(this.wxInfo) .onChange(value => { - this.wxInfo = value; + this.wxInfo = value; }) - .inputEl.setAttr('style', 'width: 520px; height: 120px;'); + .inputEl.setAttr('style', 'width: 520px; height: 120px;'); }) - - new Setting(containerEl).addButton(button => { - button.setButtonText(buttonText); - button.onClick(async () => { - if (isClear) { - isRealClear = true; - isClear = false; - button.setButtonText('确认清空?'); - } - else if (isRealClear) { - isRealClear = false; - isClear = false; - this.clear(); - button.setButtonText('保存公众号信息'); - } - else { - button.setButtonText('保存中...'); - if (await this.encrypt()) { - isClear = true; - isRealClear = false; - button.setButtonText('清空公众号信息'); + .addButton(button => { + button.setButtonText(buttonText); + button.onClick(async () => { + if (isClear) { + isRealClear = true; + isClear = false; + button.setButtonText('确认清空?'); } - else { + else if (isRealClear) { + isRealClear = false; + isClear = false; + this.clear(); button.setButtonText('保存公众号信息'); } - } - }); - }) - .addButton(button => { - button.setButtonText('测试公众号'); - button.onClick(async () => { - button.setButtonText('测试中...'); - await this.testWXInfo(); - button.setButtonText('测试公众号'); + else { + button.setButtonText('保存中...'); + if (await this.encrypt()) { + isClear = true; + isRealClear = false; + button.setButtonText('清空公众号信息'); + } + else { + button.setButtonText('保存公众号信息'); + } + } + }); }) - }) + .addButton(button => { + button.setButtonText('测试公众号'); + button.onClick(async () => { + button.setButtonText('测试中...'); + await this.testWXInfo(); + button.setButtonText('测试公众号'); + }); + }); +// + const helpEl = panel.createEl('div', { cls: 'setting-help-section' }); + helpEl.createEl('h2', { text: '帮助文档', cls: 'setting-help-title' }); + helpEl.createEl('a', { text: 'https://sunboshi.tech/doc', attr: { href: 'https://sunboshi.tech/doc' } }); } } diff --git a/src/settings.ts b/src/settings.ts index 705ab82..8db54bc 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -32,6 +32,7 @@ export class NMPSettings { excalidrawToPNG: boolean; isLoaded: boolean = false; enableEmptyLine: boolean = false; + needOpenComment: boolean = true; enableMarkdownImageToWikilink: boolean = true; // 自动将 ![alt](path/file.ext) 转为 ![[file.ext]] // gallery 相关配置:根目录前缀 & 选取图片数量 galleryPrePath: string; @@ -81,6 +82,7 @@ export class NMPSettings { this.excalidrawToPNG = false; this.expertSettingsNote = ''; this.enableEmptyLine = false; + this.needOpenComment = true; this.enableMarkdownImageToWikilink = true; // 默认值:用户原先硬编码路径 & 前 2 张 this.galleryPrePath = '/Users/gavin/myweb/static'; @@ -131,6 +133,7 @@ export class NMPSettings { expertSettingsNote, ignoreEmptyLine, enableMarkdownImageToWikilink, + needOpenComment, galleryPrePath, galleryNumPic, defaultCoverPic, @@ -160,6 +163,7 @@ export class NMPSettings { 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)); @@ -197,6 +201,7 @@ export class NMPSettings { 'excalidrawToPNG': settings.excalidrawToPNG, 'expertSettingsNote': settings.expertSettingsNote, 'ignoreEmptyLine': settings.enableEmptyLine, + 'needOpenComment': settings.needOpenComment, 'enableMarkdownImageToWikilink': settings.enableMarkdownImageToWikilink, 'galleryPrePath': settings.galleryPrePath, 'galleryNumPic': settings.galleryNumPic, diff --git a/styles.css b/styles.css index 6a22be2..ee6c919 100644 --- a/styles.css +++ b/styles.css @@ -183,6 +183,53 @@ label:hover { color: var(--c-primary); } box-shadow: 0 2px 6px rgba(30, 136, 229, 0.2); } +.nmp-settings-tabs { + display: flex; + gap: 8px; + margin-top: 16px; + border-bottom: 1px solid var(--c-border); + padding-bottom: 8px; + flex-wrap: wrap; +} + +.nmp-settings-tab-button { + border: none; + border-radius: 6px; + padding: 6px 14px; + font-size: 13px; + font-weight: 500; + color: var(--c-text-muted); + background: transparent; + cursor: pointer; + transition: all 0.2s ease; +} + +.nmp-settings-tab-button:hover { + color: var(--c-primary); + background: rgba(30, 136, 229, 0.08); +} + +.nmp-settings-tab-button.is-active { + color: var(--c-primary); + background: white; + box-shadow: var(--shadow-sm); +} + +.nmp-settings-panels { + margin-top: 16px; +} + +.nmp-settings-panel { + display: none; + gap: 12px; + flex-direction: column; +} + +.nmp-settings-panel.is-active { + display: flex; + flex-direction: column; +} + /* focus 规则见与 .upload-input:focus 的组合声明 */ .msg-view { @@ -351,10 +398,10 @@ label:hover { color: var(--c-primary); } "cover-select cover-select cover-select cover-select cover-input cover-input" "style-label style-select highlight-label highlight-select highlight-select highlight-select" "content content content content content content"; - gap: 5px; + gap: 12px; background: var(--grad-toolbar); border-radius: 12px; - padding: 5px; + padding: 16px; box-shadow: var(--shadow-sm); min-height: 0; } diff --git a/todolist.md b/todolist.md index 070e749..2ae49af 100644 --- a/todolist.md +++ b/todolist.md @@ -56,6 +56,9 @@ - 点击"全部页切图",把所有html页面转为png图片,图片保存路径和命名按此前设置。 ✅ +3. 整合xhs登陆和发布功能。![登录项目](https://biboer.cn/gitea/gavin/xhslogin/src/branch/main/README.md) + - + ## 问题 1. "发布平台"首次选“小红书”时,预览页面没有加载当前文章。 ✅ @@ -126,4 +129,3 @@ SOLVE:obsidian控制台打印信息,定位在哪里阻塞,AI修复。 自己写布局demo原型,让codex参考布局修改(原来元素美化的css可保留)。 demo原型可以手绘后,拍照让chatgpt生成,在此基础上自己修改。 -