@@ -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 ', att r: { href : 'https://sunboshi.tech/doc' } } ) ;
const tabs = [
{ id : 'style' , label : '样式 ' , render : ( panel : HTMLElement ) = > this . renderStyleTab ( panel ) } ,
{ id : 'shortcode' , label : '短代码 ' , rende r: ( 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 ( le t s of styles ) {
dropdown . addOption ( s . className , s . name ) ;
}
const styles = this . plugin . assetsManager . themes ;
for ( cons t 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 ( containerE l)
new Setting ( pane l)
. 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 ( containerE l)
new Setting ( pane l)
. 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 ( containerE l)
new Setting ( pane l)
. 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 ( containerE l)
new Setting ( pane l)
. 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 ( containerE l)
new Setting ( pane l)
. 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 ( containerE l)
new Setting ( pane l)
. setName ( 'Gallery 选取图片数' )
. setDesc ( '每个 gallery 短代码最多替换为前 N 张图片' )
. addText ( text = > {
@@ -281,7 +323,18 @@ export class NoteToMpSettingTab extends PluginSettingTab {
text . inputEl . setAttr ( 'style' , 'width: 120px;' ) ;
} ) ;
new Setting ( containerE l)
new Setting ( pane l)
. 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 ( containerE l)
new Setting ( pane l)
. setName ( '切图宽度' )
. setDesc ( '长图及切图的宽度( 像素) , 默认: 1080' )
. addText ( text = > {
@@ -348,7 +452,7 @@ export class NoteToMpSettingTab extends PluginSettingTab {
text . inputEl . setAttr ( 'style' , 'width: 120px;' ) ;
} ) ;
new Setting ( containerE l)
new Setting ( pane l)
. 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 ( containerE l)
. setName ( 'Excalidraw 渲染为 PNG 图片 ' )
. setDesc ( '开启:将 Excalidraw 笔记/嵌入转换为位图 PNG 插入;关闭:保持原始 SVG/矢量渲染(更清晰,体积更小)。' )
. addToggle ( toggle = > {
toggle . setValue ( this . settings . excalidrawToPNG ) ;
toggle . onCha nge ( async ( value ) = > {
this . settings . excalidrawToPNG = value ;
await this . plugin . saveSettings ( ) ;
new Setting ( pane l)
. setName ( '渲染图片标题 ' )
. addToggle ( toggle = > {
toggle . setValue ( this . settings . useFigcaption ) ;
toggle . onChange ( async ( value ) = > {
this . setti ngs . useFigcaption = value ;
await this . plugin . saveSettings ( ) ;
} ) ;
} ) ;
} )
new Setting ( containerE l)
new Setting ( pane l)
. 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 = '使用指南:<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 ) . add Button( button = > {
button . setButtonText ( buttonText ) ;
button . onClick ( async ( ) = > {
if ( isClear ) {
isReal Clear = tru e;
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 . set ButtonText ( buttonText ) ;
button . onClick ( async ( ) = > {
if ( isClear ) {
isRealClear = true ;
isClear = fals e;
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' } } ) ;
}
}