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; // 自动将  转为 ![[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登陆和发布功能。
+ -
+
## 问题
1. "发布平台"首次选“小红书”时,预览页面没有加载当前文章。
✅
@@ -126,4 +129,3 @@ SOLVE:obsidian控制台打印信息,定位在哪里阻塞,AI修复。
自己写布局demo原型,让codex参考布局修改(原来元素美化的css可保留)。
demo原型可以手绘后,拍照让chatgpt生成,在此基础上自己修改。
-