Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d52f79e0c | ||
|
|
0ab20de880 | ||
|
|
1db58695e8 |
@@ -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 风格。
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
如n=2,取出的图片为xx.jpg,yy.png,那么把{{<gallery dir="/img/guanzhan/1" figcaption="毕业展"/>}}{{<load-photoswipe>}}替换为:
|
||||
![[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([^>]*?)/\]` `<span style="font-style: italic; font-size: 14px; background-color: #f5f5f5; padding: 2px;">$1</span>`
|
||||
`\|\| (.*)` `<p style="font-family:'Microsoft YaHei',sans-serif;background-color:#E5E4E2 ;padding:10px;border-radius:20px;line-height:30px;">$1</p>`
|
||||
`\|\|r (.*)` `<p style="font-family:'Microsoft YaHei',sans-serif;color:white;background-color:#6F4E37;padding:10px;border-radius:20px;line-height:30px;">$1</p>`
|
||||
@@ -105,7 +105,9 @@ views:
|
||||
```
|
||||
✅
|
||||
|
||||
10. 默认选择“原创”“允许留言”。
|
||||
10. 默认选择
|
||||
- “允许留言” ✅
|
||||
- “原创”
|
||||
|
||||
11. gallery短代码增加是否使用dir中的所有图片的开关。mppickall=1,选取dir中的所有图片,mppickall=0,按“选取图片数”配置选取图片数量。
|
||||
{{<gallery dir="/img/guanzhan/1" figcaption="毕业展" mppickall=1/>}}{{<load-photoswipe>}}
|
||||
@@ -136,3 +138,7 @@ Orientation : 6 -- 图片左旋90度,需右选90才正常。
|
||||
为了规避这个问题,图片不做旋转处理,直接转为png上传公众号。解决。因为PNG不带orientation信息。
|
||||
✅
|
||||
|
||||
- 封面图片没有正确旋转。
|
||||
公众号上传仍然有图片旋转问题,同样需要取png图片避免旋转问题
|
||||
✅
|
||||
|
||||
|
||||
@@ -61,6 +61,11 @@
|
||||
|
||||
现在分页依据真实渲染高度,预览窗口内不会再丢失底部内容。建议在小红书预览里多翻几页、调整字号后重新分页验证结果。
|
||||
|
||||
## v1.3.9
|
||||
## v1.3.10
|
||||
重构xhs和wechat布局,统一使用grid,便于维护。
|
||||
|
||||
## v1.3.11
|
||||
修改wechat封面旋转问题。对全局配置进行了重构,分页显示,更加清晰。
|
||||
|
||||
封面图片先转位png解决旋转问题。
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
@@ -38,17 +39,29 @@ export async function UploadImageToWx(data: Blob, filename: string, token: strin
|
||||
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 != '') {
|
||||
const watermarkData = await AssetsManager.getInstance().readFileBinary(watermark);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ interface PlatformInfo {
|
||||
* 支持的平台列表
|
||||
*/
|
||||
const SUPPORTED_PLATFORMS: PlatformInfo[] = [
|
||||
{ value: 'wechat', label: '微信公众号', icon: '📱' },
|
||||
{ value: 'wechat', label: '公众号', icon: '📱' },
|
||||
{ value: 'xiaohongshu', label: '小红书', icon: '📔' }
|
||||
];
|
||||
|
||||
|
||||
@@ -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<string, HTMLElement>();
|
||||
const buttonMap = new Map<string, HTMLButtonElement>();
|
||||
|
||||
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('用于 {{<gallery dir="..."/>}} 短代码解析;需指向本地图片根目录')
|
||||
.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 = '使用指南:<a href="https://sunboshi.tech/customcss">https://sunboshi.tech/customcss</a>';
|
||||
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 = '使用指南:<a href="https://sunboshi.tech/expert">https://sunboshi.tech/expert</a>';
|
||||
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 => {
|
||||
@@ -361,181 +465,118 @@ 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(panel)
|
||||
.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('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(containerEl)
|
||||
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 = '使用指南:<a href="https://sunboshi.tech/customcss">https://sunboshi.tech/customcss</a>';
|
||||
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 = '使用指南:<a href="https://sunboshi.tech/expert">https://sunboshi.tech/expert</a>';
|
||||
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 = '详情说明:<a href="https://sunboshi.tech/subscribe">https://sunboshi.tech/subscribe</a>';
|
||||
if (this.settings.isVip) {
|
||||
descHtml = '<span style="color:rgb(245, 70, 85);font-weight: bold;">👑永久会员</span><br/>' + descHtml;
|
||||
}
|
||||
else if (this.settings.expireat) {
|
||||
const timestr = this.settings.expireat.toLocaleString();
|
||||
descHtml = `有效期至:${timestr} <br/>${descHtml}`
|
||||
descHtml = `有效期至:${timestr} <br/>${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' } });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
51
styles.css
51
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;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,9 @@
|
||||
- 点击"全部页切图",把所有html页面转为png图片,图片保存路径和命名按此前设置。
|
||||
✅
|
||||
|
||||
3. 整合xhs登陆和发布功能。
|
||||
-
|
||||
|
||||
## 问题
|
||||
1. "发布平台"首次选“小红书”时,预览页面没有加载当前文章。
|
||||
✅
|
||||
@@ -126,4 +129,3 @@ SOLVE:obsidian控制台打印信息,定位在哪里阻塞,AI修复。
|
||||
自己写布局demo原型,让codex参考布局修改(原来元素美化的css可保留)。
|
||||
demo原型可以手绘后,拍照让chatgpt生成,在此基础上自己修改。
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user