Compare commits
9 Commits
v1.3.9
...
5e1351408e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5e1351408e | ||
|
|
93afc99e7d | ||
|
|
b9feb2f764 | ||
|
|
97a70bc23b | ||
|
|
437619cfff | ||
|
|
10ef109353 | ||
|
|
1d52f79e0c | ||
|
|
0ab20de880 | ||
|
|
1db58695e8 |
46
README.md
46
README.md
@@ -1,8 +1,8 @@
|
|||||||
## 更新说明
|
## 更新说明
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> NoteToMP 1.3.0版本对主题进行了优化,升级后请先清理旧版本主题文件,再重新下载新版主题。
|
> Note2Any 1.3.0版本对主题进行了优化,升级后请先清理旧版本主题文件,再重新下载新版主题。
|
||||||
>
|
>
|
||||||
> 操作步骤:在NoteToMP插件设置中,先点击『清空主题-清空』,然后点击『获取更多主题-下载』
|
> 操作步骤:在Note2Any插件设置中,先点击『清空主题-清空』,然后点击『获取更多主题-下载』
|
||||||
>
|
>
|
||||||
> 注意:如果修改过主题文件请做备份后再操作。
|
> 注意:如果修改过主题文件请做备份后再操作。
|
||||||
|
|
||||||
@@ -49,30 +49,30 @@
|
|||||||
```
|
```
|
||||||
路径日志做了节流:同一文件 3 秒内不重复打印。后续可加"调试开关"以完全关闭。
|
路径日志做了节流:同一文件 3 秒内不重复打印。后续可加"调试开关"以完全关闭。
|
||||||
|
|
||||||
### 摘要、封面裁剪、原文链接等ges/screenshot.png)
|
### 摘要、封面裁剪、原文链接等
|
||||||
|
|
||||||
## 2、安装
|
## 2、安装
|
||||||
首先,**请确认已关闭了Obsidian的安全模式**。如未关闭,请通过**设置——第三方插件——关闭安全模式**关闭。
|
首先,**请确认已关闭了Obsidian的安全模式**。如未关闭,请通过**设置——第三方插件——关闭安全模式**关闭。
|
||||||
### 2.1 插件安装
|
### 2.1 插件安装
|
||||||
|
|
||||||
#### 从官方**社区插件市场**安装
|
#### 从官方**社区插件市场**安装
|
||||||
通过Obsidian**设置——第三方插件——社区插件市场**,输入**NoteToMP**搜索安装。
|
通过Obsidian**设置——第三方插件——社区插件市场**,输入**Note2Any**搜索安装。
|
||||||
|
|
||||||
### 2.2 主题资源安装
|
### 2.2 主题资源安装
|
||||||
如果采用的是用从插件市场或者Github下载安装的方式,在插件安装完成后还需要再下载主题资源。网盘里的安装包已经集成了主题样式,无需下载。
|
如果采用的是用从插件市场或者Github下载安装的方式,在插件安装完成后还需要再下载主题资源。网盘里的安装包已经集成了主题样式,无需下载。
|
||||||
|
|
||||||
**1)通过设置下载**
|
**1)通过设置下载**
|
||||||
为了尽可能保证插件符合官方规范,主题和代码高亮需要打开Obsidian的**设置**界面,在底部的**第三方插件**——**Note to MP**——**获取更多主题**手动下载。
|
为了尽可能保证插件符合官方规范,主题和代码高亮需要打开Obsidian的**设置**界面,在底部的**第三方插件**——**Note 2 Any**——**获取更多主题**手动下载。
|
||||||
|
|
||||||
**2)手动下载**
|
**2)手动下载**
|
||||||
也可以直接在[Release](https://github.com/sunbooshi/note-to-mp/releases)页面下载`assets.zip`文件,解压后放到`.obsidian/plugins/note-to-mp/assets`目录下。
|
也可以直接在[Release](https://biboer.cn/gitea/gavin/note2any/releases)页面下载`assets.zip`文件,解压后放到`.obsidian/plugins/note2any/assets`目录下。
|
||||||
|
|
||||||
### 2.3 常见安装问题
|
### 2.3 常见安装问题
|
||||||
|
|
||||||
**只有默认主题**
|
**只有默认主题**
|
||||||
确认根据**2.2 主题资源安装**里的步骤操作了,然后检查一下插件目录内容,应如下所示:
|
确认根据**2.2 主题资源安装**里的步骤操作了,然后检查一下插件目录内容,应如下所示:
|
||||||
```
|
```
|
||||||
.obsidian/plugins/note-to-mp/
|
.obsidian/plugins/note2any/
|
||||||
├── assets
|
├── assets
|
||||||
│ ├── themes.json
|
│ ├── themes.json
|
||||||
│ ├── highlights.json
|
│ ├── highlights.json
|
||||||
@@ -177,36 +177,36 @@ c=+-sqrt(a^2+b^2)
|
|||||||
```
|
```
|
||||||
````
|
````
|
||||||
|
|
||||||
然后打开NoteToMP插件设置,将**自定义样式**(即包含自定义CSS内容的笔记名称),粘贴到**自定义CSS笔记**中即可。如果不使用自定义CSS,留空即可。
|
然后打开Note2Any插件设置,将**自定义样式**(即包含自定义CSS内容的笔记名称),粘贴到**自定义CSS笔记**中即可。如果不使用自定义CSS,留空即可。
|
||||||
|
|
||||||
关于自定义CSS的写法可以参考下面的代码
|
关于自定义CSS的写法可以参考下面的代码
|
||||||
```css
|
```css
|
||||||
/* 全局属性
|
/* 全局属性
|
||||||
* 这里可以设置字体,字体大小,边距,背景颜色等
|
* 这里可以设置字体,字体大小,边距,背景颜色等
|
||||||
*/
|
*/
|
||||||
.note-to-mp {
|
.note2any {
|
||||||
/* 注:请在大括号内改写!!! */
|
/* 注:请在大括号内改写!!! */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 段落 */
|
/* 段落 */
|
||||||
.note-to-mp p {
|
.note2any p {
|
||||||
/* 注:请在大括号内改写!!! */
|
/* 注:请在大括号内改写!!! */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 一级标题 */
|
/* 一级标题 */
|
||||||
.note-to-mp h1 {
|
.note2any h1 {
|
||||||
/* 注:请在大括号内改写!!! */
|
/* 注:请在大括号内改写!!! */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 二级标题 */
|
/* 二级标题 */
|
||||||
.note-to-mp h2 {
|
.note2any h2 {
|
||||||
/* 注:请在大括号内改写!!! */
|
/* 注:请在大括号内改写!!! */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* 三级标题 */
|
/* 三级标题 */
|
||||||
.note-to-mp h3 {
|
.note2any h3 {
|
||||||
/* 注:请在大括号内改写!!! */
|
/* 注:请在大括号内改写!!! */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,38 +214,38 @@ c=+-sqrt(a^2+b^2)
|
|||||||
/* 无序列表整体样式
|
/* 无序列表整体样式
|
||||||
* list-style-type: square|circle|disc;
|
* list-style-type: square|circle|disc;
|
||||||
*/
|
*/
|
||||||
.note-to-mp ul {
|
.note2any ul {
|
||||||
/* 注:请在大括号内改写!!! */
|
/* 注:请在大括号内改写!!! */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 加粗 */
|
/* 加粗 */
|
||||||
.note-to-mp strong {
|
.note2any strong {
|
||||||
/* 注:请在大括号内改写!!! */
|
/* 注:请在大括号内改写!!! */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 斜体 */
|
/* 斜体 */
|
||||||
.note-to-mp em {
|
.note2any em {
|
||||||
/* 注:请在大括号内改写!!! */
|
/* 注:请在大括号内改写!!! */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 加粗斜体 */
|
/* 加粗斜体 */
|
||||||
.note-to-mp em strong {
|
.note2any em strong {
|
||||||
/* 注:请在大括号内改写!!! */
|
/* 注:请在大括号内改写!!! */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 删除线 */
|
/* 删除线 */
|
||||||
.note-to-mp del {
|
.note2any del {
|
||||||
/* 注:请在大括号内改写!!! */
|
/* 注:请在大括号内改写!!! */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 分隔线
|
/* 分隔线
|
||||||
*/
|
*/
|
||||||
.note-to-mp hr {
|
.note2any hr {
|
||||||
/* 注:请在大括号内改写!!! */
|
/* 注:请在大括号内改写!!! */
|
||||||
}
|
}
|
||||||
/* 图片
|
/* 图片
|
||||||
*/
|
*/
|
||||||
.note-to-mp img {
|
.note2any img {
|
||||||
/* 注:请在大括号内改写!!! */
|
/* 注:请在大括号内改写!!! */
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
@@ -288,7 +288,7 @@ c=+-sqrt(a^2+b^2)
|
|||||||
|
|
||||||
例如这篇文章[几个让公众号排版更精致的小技巧,手机上也可以!](https://mp.weixin.qq.com/s/Q4_pV9TW8un3qZ0vrUvD1A)👈️使用的自定义样式如下:
|
例如这篇文章[几个让公众号排版更精致的小技巧,手机上也可以!](https://mp.weixin.qq.com/s/Q4_pV9TW8un3qZ0vrUvD1A)👈️使用的自定义样式如下:
|
||||||
```css
|
```css
|
||||||
.note-to-mp {
|
.note2any {
|
||||||
font-family: Optima-regular, Optima, "Microsoft YaHei", PingFangSC-regular, serif;
|
font-family: Optima-regular, Optima, "Microsoft YaHei", PingFangSC-regular, serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,7 +313,7 @@ section .note-callout-example {
|
|||||||
background-color: rgba(90, 185, 131, 0.1);
|
background-color: rgba(90, 185, 131, 0.1);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
上面的例子,通过`.note-to-mp`指定了文章的字体,通过`h2 strong`单独定义了h2标题下strong的样式,这样可以在标题中通过使用粗体增加了一个边框样式。通过`h2`定义了h2标题的底部线条的宽度和文本颜色。这样配合**Olive Dunk**主题就形成了自己的风格。
|
上面的例子,通过`.note2any`指定了文章的字体,通过`h2 strong`单独定义了h2标题下strong的样式,这样可以在标题中通过使用粗体增加了一个边框样式。通过`h2`定义了h2标题的底部线条的宽度和文本颜色。这样配合**Olive Dunk**主题就形成了自己的风格。
|
||||||
### 公众号名片
|
### 公众号名片
|
||||||
|
|
||||||
请参考 https://mp.weixin.qq.com/s/1wYd15Irmv9BPabgp5XMCA
|
请参考 https://mp.weixin.qq.com/s/1wYd15Irmv9BPabgp5XMCA
|
||||||
@@ -341,7 +341,7 @@ NoteToMP插件支持该语法。
|
|||||||
![[文件名称#^段落标记]]
|
![[文件名称#^段落标记]]
|
||||||
```
|
```
|
||||||
|
|
||||||
在NoteToMP插件中有两种展示文件嵌入内容的样式,一种是引用,也就是Obsidian默认的方式,一种是正文,相当于模板的方式。与模板不同的是,采用嵌入方式内容会跟随被嵌入文件的内容更改。
|
在Note2Any插件中有两种展示文件嵌入内容的样式,一种是引用,也就是Obsidian默认的方式,一种是正文,相当于模板的方式。与模板不同的是,采用嵌入方式内容会跟随被嵌入文件的内容更改。
|
||||||
|
|
||||||
## 批量发布(Batch Publish)
|
## 批量发布(Batch Publish)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
.note-to-mp {
|
.note2any {
|
||||||
padding: 0 1em;
|
padding: 0 1em;
|
||||||
color: #595959;
|
color: #595959;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
|||||||
2
build.sh
2
build.sh
@@ -22,7 +22,7 @@ else
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# 2. 目标目录
|
# 2. 目标目录
|
||||||
PLUGIN_DIR=~/myweb/.obsidian/plugins/note-to-mp
|
PLUGIN_DIR=~/myweb/.obsidian/plugins/note2any
|
||||||
FILES=("main.js" "styles.css" "manifest.json")
|
FILES=("main.js" "styles.css" "manifest.json")
|
||||||
|
|
||||||
# 3. 遍历文件,逐一备份并覆盖
|
# 3. 遍历文件,逐一备份并覆盖
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ The format is based on Keep a Changelog, and this project adheres to Semantic Ve
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- README:新增图片方向处理说明、Gallery 参数使用示例。
|
- README:新增图片方向处理说明、Gallery 参数使用示例。
|
||||||
|
- 预览布局:微信与小红书界面改为统一的网格化布局(`wechat-board` / `xhs-board`),组件按区域栅格排列,便于维护与对齐。
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
- 若遇到其他 EXIF 方向值(除 1/3/6/8),当前保持原样,可后续扩展。
|
- 若遇到其他 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 模板。
|
- 发布新版本:更新 `package.json` / `manifest.json` 的版本号;追加 `versions.json`;将当前 Unreleased 条目移动为新的版本号,并添加日期;再创建新的 Unreleased 模板。
|
||||||
- 提交信息建议:`feat: ...`, `fix: ...`, `docs: ...`, `refactor: ...` 等 Conventional Commits 风格。
|
- 提交信息建议:`feat: ...`, `fix: ...`, `docs: ...`, `refactor: ...` 等 Conventional Commits 风格。
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
如n=2,取出的图片为xx.jpg,yy.png,那么把{{<gallery dir="/img/guanzhan/1" figcaption="毕业展"/>}}{{<load-photoswipe>}}替换为:
|
如n=2,取出的图片为xx.jpg,yy.png,那么把{{<gallery dir="/img/guanzhan/1" figcaption="毕业展"/>}}{{<load-photoswipe>}}替换为:
|
||||||
![[xx.jpg]]
|
![[xx.jpg]]
|
||||||
![[yy.png]]
|
![[yy.png]]
|
||||||
✅
|
✅
|
||||||
|
|
||||||
3.
|
3.
|
||||||
对如下:
|
对如下:
|
||||||
@@ -46,7 +46,7 @@ src可能使用link:
|
|||||||
✅
|
✅
|
||||||
|
|
||||||
4.
|
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>`
|
`\[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>`
|
`\|\| (.*)` `<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>`
|
`\|\|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,按“选取图片数”配置选取图片数量。
|
11. gallery短代码增加是否使用dir中的所有图片的开关。mppickall=1,选取dir中的所有图片,mppickall=0,按“选取图片数”配置选取图片数量。
|
||||||
{{<gallery dir="/img/guanzhan/1" figcaption="毕业展" mppickall=1/>}}{{<load-photoswipe>}}
|
{{<gallery dir="/img/guanzhan/1" figcaption="毕业展" mppickall=1/>}}{{<load-photoswipe>}}
|
||||||
@@ -136,3 +138,7 @@ Orientation : 6 -- 图片左旋90度,需右选90才正常。
|
|||||||
为了规避这个问题,图片不做旋转处理,直接转为png上传公众号。解决。因为PNG不带orientation信息。
|
为了规避这个问题,图片不做旋转处理,直接转为png上传公众号。解决。因为PNG不带orientation信息。
|
||||||
✅
|
✅
|
||||||
|
|
||||||
|
- 封面图片没有正确旋转。
|
||||||
|
公众号上传仍然有图片旋转问题,同样需要取png图片避免旋转问题
|
||||||
|
✅
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
"id": "note-to-mp",
|
"id": "note2any",
|
||||||
"name": "NoteToAny",
|
"name": "Note2Any",
|
||||||
"version": "1.3.4",
|
"version": "1.3.4",
|
||||||
"minAppVersion": "1.4.5",
|
"minAppVersion": "1.4.5",
|
||||||
"description": "xiaohongshu/mp publisher ",
|
"description": "xiaohongshu/mp publisher ",
|
||||||
"author": "Gavin chan",
|
"author": "Gavin chan",
|
||||||
"authorUrl": "https://biboer.cn",
|
"authorUrl": "https://biboer.cn",
|
||||||
"isDesktopOnly": false
|
"isDesktopOnly": false,
|
||||||
|
"fundingUrl": "https://biboer.cn/gitea/gavin/note2any"
|
||||||
}
|
}
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -1,8 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "note-to-mp",
|
"name": "note2any",
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"description": "This is a plugin for Obsidian (https://obsidian.md)",
|
"description": "This is a plugin for Obsidian (https://obsidian.md)",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://biboer.cn/gitea/gavin/note2any.git"
|
||||||
|
},
|
||||||
|
"homepage": "https://biboer.cn/gitea/gavin/note2any",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://biboer.cn/gitea/gavin/note2any/issues"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "node esbuild.config.mjs",
|
"dev": "node esbuild.config.mjs",
|
||||||
"build": "npm run build:bundle --",
|
"build": "npm run build:bundle --",
|
||||||
|
|||||||
@@ -61,6 +61,11 @@
|
|||||||
|
|
||||||
现在分页依据真实渲染高度,预览窗口内不会再丢失底部内容。建议在小红书预览里多翻几页、调整字号后重新分页验证结果。
|
现在分页依据真实渲染高度,预览窗口内不会再丢失底部内容。建议在小红书预览里多翻几页、调整字号后重新分页验证结果。
|
||||||
|
|
||||||
## v1.3.9
|
## v1.3.10
|
||||||
重构xhs和wechat布局,统一使用grid,便于维护。
|
重构xhs和wechat布局,统一使用grid,便于维护。
|
||||||
|
|
||||||
|
## v1.3.11
|
||||||
|
修改wechat封面旋转问题。对全局配置进行了重构,分页显示,更加清晰。
|
||||||
|
|
||||||
|
封面图片先转位png解决旋转问题。
|
||||||
|
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ export class ArticleRender implements MDRendererCallback {
|
|||||||
|
|
||||||
errorContent(error: any) {
|
errorContent(error: any) {
|
||||||
return '<h1>渲染失败!</h1><br/>'
|
return '<h1>渲染失败!</h1><br/>'
|
||||||
+ '如需帮助请前往 <a href="https://github.com/sunbooshi/note-to-mp/issues">https://github.com/sunbooshi/note-to-mp/issues</a> 反馈<br/><br/>'
|
+ '如需帮助请前往 <a href="https://biboer.cn/gitea/gavin/note2any/issues">https://biboer.cn/gitea/gavin/note2any/issues</a> 反馈<br/><br/>'
|
||||||
+ '如果方便,请提供引发错误的完整Markdown内容。<br/><br/>'
|
+ '如果方便,请提供引发错误的完整Markdown内容。<br/><br/>'
|
||||||
+ '<br/>Obsidian版本:' + apiVersion
|
+ '<br/>Obsidian版本:' + apiVersion
|
||||||
+ '<br/>错误信息:<br/>'
|
+ '<br/>错误信息:<br/>'
|
||||||
@@ -341,11 +341,11 @@ export class ArticleRender implements MDRendererCallback {
|
|||||||
try {
|
try {
|
||||||
const globalAny = window as any;
|
const globalAny = window as any;
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (!globalAny.__note2mp_lastPathLog ||
|
if (!globalAny.__note2any_lastPathLog ||
|
||||||
globalAny.__note2mp_lastPathLog.path !== file.path ||
|
globalAny.__note2any_lastPathLog.path !== file.path ||
|
||||||
now - globalAny.__note2mp_lastPathLog.time > 3000) {
|
now - globalAny.__note2any_lastPathLog.time > 3000) {
|
||||||
console.log('[note2mp] active file path:', file.path);
|
console.log('[note2any] active file path:', file.path);
|
||||||
globalAny.__note2mp_lastPathLog = { path: file.path, time: now };
|
globalAny.__note2any_lastPathLog = { path: file.path, time: now };
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
const metadata = this.app.metadataCache.getFileCache(file);
|
const metadata = this.app.metadataCache.getFileCache(file);
|
||||||
@@ -381,7 +381,9 @@ export class ArticleRender implements MDRendererCallback {
|
|||||||
res.cover = undefined; // 忽略 frontmatter
|
res.cover = undefined; // 忽略 frontmatter
|
||||||
}
|
}
|
||||||
res.thumb_media_id = this.getFrontmatterValue(frontmatter, keys.thumb_media_id);
|
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.only_fans_can_comment = frontmatter[keys.only_fans_can_comment] ? 1 : undefined;
|
||||||
res.appid = this.getFrontmatterValue(frontmatter, keys.appid);
|
res.appid = this.getFrontmatterValue(frontmatter, keys.appid);
|
||||||
if (res.appid && !res.appid.startsWith('wx')) {
|
if (res.appid && !res.appid.startsWith('wx')) {
|
||||||
@@ -473,11 +475,14 @@ export class ArticleRender implements MDRendererCallback {
|
|||||||
if (base) res.cover = `![[${base}]]`;
|
if (base) res.cover = `![[${base}]]`;
|
||||||
}
|
}
|
||||||
if (res.cover) {
|
if (res.cover) {
|
||||||
try { console.log('[note2mp] use default cover:', def, '->', res.cover); } catch {}
|
try { console.log('[note2any] use default cover:', def, '->', res.cover); } catch {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (res.need_open_comment === undefined) {
|
||||||
|
res.need_open_comment = this.settings.needOpenComment ? 1 : 0;
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -717,7 +722,7 @@ export class ArticleRender implements MDRendererCallback {
|
|||||||
article_type: 'newspic',
|
article_type: 'newspic',
|
||||||
title: metadata.title || this.title,
|
title: metadata.title || this.title,
|
||||||
content: content,
|
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,
|
only_fans_can_comment: metadata.only_fans_can_comment || 0,
|
||||||
image_info: {
|
image_info: {
|
||||||
image_list: imageList,
|
image_list: imageList,
|
||||||
|
|||||||
@@ -297,7 +297,7 @@ export default class AssetsManager {
|
|||||||
|
|
||||||
getThemeURL() {
|
getThemeURL() {
|
||||||
const version = this.manifest.version;
|
const version = this.manifest.version;
|
||||||
return `https://github.com/sunbooshi/note-to-mp/releases/download/${version}/assets.zip`;
|
return `https://biboer.cn/gitea/gavin/note2any/releases/download/${version}/assets.zip`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getStyle() {
|
async getStyle() {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import { App, Modal, Setting, TFile, Notice, ButtonComponent } from 'obsidian';
|
import { App, Modal, Setting, TFile, Notice, ButtonComponent } from 'obsidian';
|
||||||
import { BatchArticleFilter, BatchFilterConfig } from './batch-filter';
|
import { BatchArticleFilter, BatchFilterConfig } from './batch-filter';
|
||||||
import NoteToMpPlugin from './main';
|
import Note2AnyPlugin from './main';
|
||||||
// 小红书功能模块
|
// 小红书功能模块
|
||||||
import { XiaohongshuContentAdapter } from './xiaohongshu/adapter';
|
import { XiaohongshuContentAdapter } from './xiaohongshu/adapter';
|
||||||
import { XiaohongshuAPIManager } from './xiaohongshu/api';
|
import { XiaohongshuAPIManager } from './xiaohongshu/api';
|
||||||
@@ -34,7 +34,7 @@ import { XiaohongshuAPIManager } from './xiaohongshu/api';
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export class BatchPublishModal extends Modal {
|
export class BatchPublishModal extends Modal {
|
||||||
plugin: NoteToMpPlugin;
|
plugin: Note2AnyPlugin;
|
||||||
filter: BatchArticleFilter;
|
filter: BatchArticleFilter;
|
||||||
filteredFiles: TFile[] = [];
|
filteredFiles: TFile[] = [];
|
||||||
selectedFiles: Set<TFile> = new Set();
|
selectedFiles: Set<TFile> = new Set();
|
||||||
@@ -69,7 +69,7 @@ export class BatchPublishModal extends Modal {
|
|||||||
orderDirection: 'asc'
|
orderDirection: 'asc'
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(app: App, plugin: NoteToMpPlugin) {
|
constructor(app: App, plugin: Note2AnyPlugin) {
|
||||||
super(app);
|
super(app);
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.filter = new BatchArticleFilter(app);
|
this.filter = new BatchArticleFilter(app);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { wxUploadImage } from "./wechat/weixin-api";
|
|||||||
import { NMPSettings } from "./settings";
|
import { NMPSettings } from "./settings";
|
||||||
import { IsWasmReady, LoadWasm } from "./wasm/wasm";
|
import { IsWasmReady, LoadWasm } from "./wasm/wasm";
|
||||||
import AssetsManager from "./assets";
|
import AssetsManager from "./assets";
|
||||||
|
import { convertJpegIfNeeded } from "./exif-orientation";
|
||||||
|
|
||||||
declare function GoWebpToJPG(data: Uint8Array): Uint8Array; // wasm 返回 Uint8Array
|
declare function GoWebpToJPG(data: Uint8Array): Uint8Array; // wasm 返回 Uint8Array
|
||||||
declare function GoWebpToPNG(data: Uint8Array): Uint8Array;
|
declare function GoWebpToPNG(data: Uint8Array): Uint8Array;
|
||||||
@@ -38,17 +39,29 @@ export async function UploadImageToWx(data: Blob, filename: string, token: strin
|
|||||||
await PrepareImageLib();
|
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;
|
const watermark = NMPSettings.getInstance().watermark;
|
||||||
if (watermark != null && watermark != '') {
|
if (watermark != null && watermark != '') {
|
||||||
const watermarkData = await AssetsManager.getInstance().readFileBinary(watermark);
|
const watermarkData = await AssetsManager.getInstance().readFileBinary(watermark);
|
||||||
if (watermarkData == null) {
|
if (watermarkData == null) {
|
||||||
throw new Error('水印图片不存在: ' + watermark);
|
throw new Error('水印图片不存在: ' + watermark);
|
||||||
}
|
}
|
||||||
const watermarkImg = AddWatermark(await data.arrayBuffer(), watermarkData);
|
const watermarkImg = AddWatermark(await data.arrayBuffer(), watermarkData);
|
||||||
// AddWatermark 返回 Uint8Array,Blob 的类型签名对某些 TS 配置可能对 ArrayBufferLike 有严格区分
|
// AddWatermark 返回 Uint8Array,Blob 的类型签名对某些 TS 配置可能对 ArrayBufferLike 有严格区分
|
||||||
// 此处使用其底层 ArrayBuffer 来构造 Blob,避免类型不兼容错误
|
// 此处使用其底层 ArrayBuffer 来构造 Blob,避免类型不兼容错误
|
||||||
const bufferPart = watermarkImg.buffer as ArrayBuffer;
|
const bufferPart = watermarkImg.buffer as ArrayBuffer;
|
||||||
data = new Blob([bufferPart], { type: data.type });
|
data = new Blob([bufferPart], { type: data.type });
|
||||||
}
|
}
|
||||||
return await wxUploadImage(data, filename, token, type);
|
return await wxUploadImage(data, filename, token, type);
|
||||||
}
|
}
|
||||||
|
|||||||
46
src/main.ts
46
src/main.ts
@@ -10,7 +10,7 @@
|
|||||||
import { Plugin, WorkspaceLeaf, App, PluginManifest, Menu, Notice, TAbstractFile, TFile, TFolder } from 'obsidian';
|
import { Plugin, WorkspaceLeaf, App, PluginManifest, Menu, Notice, TAbstractFile, TFile, TFolder } from 'obsidian';
|
||||||
import { PreviewView, VIEW_TYPE_NOTE_PREVIEW } from './preview-view';
|
import { PreviewView, VIEW_TYPE_NOTE_PREVIEW } from './preview-view';
|
||||||
import { NMPSettings } from './settings';
|
import { NMPSettings } from './settings';
|
||||||
import { NoteToMpSettingTab } from './setting-tab';
|
import { Note2AnySettingTab } from './setting-tab';
|
||||||
import AssetsManager from './assets';
|
import AssetsManager from './assets';
|
||||||
import { setVersion, uevent } from './utils';
|
import { setVersion, uevent } from './utils';
|
||||||
import { WidgetsModal } from './widgets-modal';
|
import { WidgetsModal } from './widgets-modal';
|
||||||
@@ -20,7 +20,7 @@ import { XiaohongshuContentAdapter } from './xiaohongshu/adapter';
|
|||||||
import { XiaohongshuAPIManager } from './xiaohongshu/api';
|
import { XiaohongshuAPIManager } from './xiaohongshu/api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NoteToMpPlugin
|
* Note2AnyPlugin
|
||||||
*
|
*
|
||||||
* 中文说明:
|
* 中文说明:
|
||||||
* 这是插件的入口类,负责:
|
* 这是插件的入口类,负责:
|
||||||
@@ -30,12 +30,12 @@ import { XiaohongshuAPIManager } from './xiaohongshu/api';
|
|||||||
* - 提供文件右键菜单扩展,支持对单文件或文件夹进行发布操作
|
* - 提供文件右键菜单扩展,支持对单文件或文件夹进行发布操作
|
||||||
*
|
*
|
||||||
* 设计决策(简要):
|
* 设计决策(简要):
|
||||||
* - 将批量发布的 UI 放在 `BatchPublishModal` 中,命令 `note-to-mp-batch-publish` 会打开该模态框
|
* - 将批量发布的 UI 放在 `BatchPublishModal` 中,命令 `note2any-batch-publish` 会打开该模态框
|
||||||
* - 单篇发布/文件夹批量发布仍复用 `NotePreview` 的发布逻辑,避免重复实现上传流程
|
* - 单篇发布/文件夹批量发布仍复用 `NotePreview` 的发布逻辑,避免重复实现上传流程
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
export default class NoteToMpPlugin extends Plugin {
|
export default class Note2AnyPlugin extends Plugin {
|
||||||
settings: NMPSettings;
|
settings: NMPSettings;
|
||||||
assetsManager: AssetsManager;
|
assetsManager: AssetsManager;
|
||||||
ribbonIconEl: HTMLElement | null = null;
|
ribbonIconEl: HTMLElement | null = null;
|
||||||
@@ -51,35 +51,35 @@ export default class NoteToMpPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async onload() {
|
async onload() {
|
||||||
console.log('Loading NoteToMP (plugin onload start)');
|
console.log('Loading Note2Any (plugin onload start)');
|
||||||
setVersion(this.manifest.version);
|
setVersion(this.manifest.version);
|
||||||
uevent('load');
|
uevent('load');
|
||||||
console.log('[NoteToMpPlugin] workspace.layoutReady at onload =', this.app.workspace.layoutReady);
|
console.log('[Note2AnyPlugin] workspace.layoutReady at onload =', this.app.workspace.layoutReady);
|
||||||
|
|
||||||
// 先注册 view 之前,防止旧 snapshot 立即恢复创建大量视图:先临时卸载残留叶子(如果类型匹配)
|
// 先注册 view 之前,防止旧 snapshot 立即恢复创建大量视图:先临时卸载残留叶子(如果类型匹配)
|
||||||
try {
|
try {
|
||||||
const legacyLeaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_NOTE_PREVIEW);
|
const legacyLeaves = this.app.workspace.getLeavesOfType(VIEW_TYPE_NOTE_PREVIEW);
|
||||||
if (legacyLeaves.length > 0) {
|
if (legacyLeaves.length > 0) {
|
||||||
console.log('[NoteToMpPlugin] detach legacy leaves early count=', legacyLeaves.length);
|
console.log('[Note2AnyPlugin] detach legacy leaves early count=', legacyLeaves.length);
|
||||||
this.app.workspace.detachLeavesOfType(VIEW_TYPE_NOTE_PREVIEW);
|
this.app.workspace.detachLeavesOfType(VIEW_TYPE_NOTE_PREVIEW);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[NoteToMpPlugin] early detach failed', e);
|
console.warn('[Note2AnyPlugin] early detach failed', e);
|
||||||
}
|
}
|
||||||
this.app.workspace.onLayoutReady(async () => {
|
this.app.workspace.onLayoutReady(async () => {
|
||||||
console.log('[NoteToMpPlugin] onLayoutReady callback entered');
|
console.log('[Note2AnyPlugin] onLayoutReady callback entered');
|
||||||
console.time('[NoteToMpPlugin] startup:onLayoutReady→loadResource');
|
console.time('[Note2AnyPlugin] startup:onLayoutReady→loadResource');
|
||||||
try {
|
try {
|
||||||
await this.loadResource(); // 确保资源完全加载完再继续,避免后续视图初始化反复等待
|
await this.loadResource(); // 确保资源完全加载完再继续,避免后续视图初始化反复等待
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[NoteToMpPlugin] loadResource 失败', e);
|
console.error('[Note2AnyPlugin] loadResource 失败', e);
|
||||||
} finally {
|
} finally {
|
||||||
console.timeEnd('[NoteToMpPlugin] startup:onLayoutReady→loadResource');
|
console.timeEnd('[Note2AnyPlugin] startup:onLayoutReady→loadResource');
|
||||||
}
|
}
|
||||||
// 清理旧视图
|
// 清理旧视图
|
||||||
this.cleanupLegacyViews();
|
this.cleanupLegacyViews();
|
||||||
// 取消自动打开预览视图(用于排查启动卡顿)。用户可通过图标或命令手动打开。
|
// 取消自动打开预览视图(用于排查启动卡顿)。用户可通过图标或命令手动打开。
|
||||||
// console.log('[NoteToMpPlugin] 已跳过自动打开预览视图调试模式');
|
// console.log('[Note2AnyPlugin] 已跳过自动打开预览视图调试模式');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.registerView(
|
this.registerView(
|
||||||
@@ -90,28 +90,28 @@ export default class NoteToMpPlugin extends Plugin {
|
|||||||
this.ribbonIconEl = this.addRibbonIcon('clipboard-paste', '复制到公众号', (evt: MouseEvent) => {
|
this.ribbonIconEl = this.addRibbonIcon('clipboard-paste', '复制到公众号', (evt: MouseEvent) => {
|
||||||
this.activateView();
|
this.activateView();
|
||||||
});
|
});
|
||||||
this.ribbonIconEl.addClass('note-to-mp-plugin-ribbon-class');
|
this.ribbonIconEl.addClass('note2any-plugin-ribbon-class');
|
||||||
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'note-to-mp-preview',
|
id: 'note2any-preview',
|
||||||
name: '复制到公众号',
|
name: '复制到公众号',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
this.activateView();
|
this.activateView();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addSettingTab(new NoteToMpSettingTab(this.app, this));
|
this.addSettingTab(new Note2AnySettingTab(this.app, this));
|
||||||
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'note-to-mp-widget',
|
id: 'note2any-widget',
|
||||||
name: '插入样式小部件',
|
name: '插入样式组件',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
new WidgetsModal(this.app).open();
|
new WidgetsModal(this.app).open();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'note-to-mp-batch-publish',
|
id: 'note2any-batch-publish',
|
||||||
name: '批量发布文章',
|
name: '批量发布文章',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
new BatchPublishModal(this.app, this).open();
|
new BatchPublishModal(this.app, this).open();
|
||||||
@@ -120,7 +120,7 @@ export default class NoteToMpPlugin extends Plugin {
|
|||||||
|
|
||||||
// TODO: 重构后需要重新实现批量发布功能
|
// TODO: 重构后需要重新实现批量发布功能
|
||||||
// this.addCommand({
|
// this.addCommand({
|
||||||
// id: 'note-to-mp-pub',
|
// id: 'note2any-pub',
|
||||||
// name: '发布公众号文章',
|
// name: '发布公众号文章',
|
||||||
// callback: async () => {
|
// callback: async () => {
|
||||||
// await this.activateView();
|
// await this.activateView();
|
||||||
@@ -130,7 +130,7 @@ export default class NoteToMpPlugin extends Plugin {
|
|||||||
|
|
||||||
// 命令:当前文件发布到微信草稿
|
// 命令:当前文件发布到微信草稿
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: 'note-to-mp-post-current',
|
id: 'note2any-post-current',
|
||||||
name: '发布当前文件到公众号草稿',
|
name: '发布当前文件到公众号草稿',
|
||||||
callback: async () => {
|
callback: async () => {
|
||||||
const file = this.app.workspace.getActiveFile();
|
const file = this.app.workspace.getActiveFile();
|
||||||
@@ -181,7 +181,7 @@ export default class NoteToMpPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onunload() {
|
onunload() {
|
||||||
console.log('Unloading NoteToMP');
|
console.log('Unloading Note2Any');
|
||||||
// 移除 ribbon icon,避免重载插件时重复创建
|
// 移除 ribbon icon,避免重载插件时重复创建
|
||||||
if (this.ribbonIconEl) {
|
if (this.ribbonIconEl) {
|
||||||
this.ribbonIconEl.remove();
|
this.ribbonIconEl.remove();
|
||||||
@@ -208,7 +208,7 @@ export default class NoteToMpPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[NoteToMp] cleanupLegacyViews 失败', e);
|
console.warn('[Note2Any] cleanupLegacyViews 失败', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ interface PlatformInfo {
|
|||||||
* 支持的平台列表
|
* 支持的平台列表
|
||||||
*/
|
*/
|
||||||
const SUPPORTED_PLATFORMS: PlatformInfo[] = [
|
const SUPPORTED_PLATFORMS: PlatformInfo[] = [
|
||||||
{ value: 'wechat', label: '微信公众号', icon: '📱' },
|
{ value: 'wechat', label: '公众号', icon: '📱' },
|
||||||
{ value: 'xiaohongshu', label: '小红书', icon: '📔' }
|
{ value: 'xiaohongshu', label: '小红书', icon: '📔' }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -4,19 +4,19 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { App, TextAreaComponent, PluginSettingTab, Setting, Notice, sanitizeHTMLToDom } from 'obsidian';
|
import { App, TextAreaComponent, PluginSettingTab, Setting, Notice, sanitizeHTMLToDom } from 'obsidian';
|
||||||
import NoteToMpPlugin from './main';
|
import Note2AnyPlugin from './main';
|
||||||
import { wxGetToken, wxEncrypt } from './wechat/weixin-api';
|
import { wxGetToken, wxEncrypt } from './wechat/weixin-api';
|
||||||
import { cleanMathCache } from './markdown/math';
|
import { cleanMathCache } from './markdown/math';
|
||||||
import { NMPSettings } from './settings';
|
import { NMPSettings } from './settings';
|
||||||
import { DocModal } from './doc-modal';
|
import { DocModal } from './doc-modal';
|
||||||
|
|
||||||
export class NoteToMpSettingTab extends PluginSettingTab {
|
export class Note2AnySettingTab extends PluginSettingTab {
|
||||||
plugin: NoteToMpPlugin;
|
plugin: Note2AnyPlugin;
|
||||||
wxInfo: string;
|
wxInfo: string;
|
||||||
wxTextArea: TextAreaComponent|null;
|
wxTextArea: TextAreaComponent|null;
|
||||||
settings: NMPSettings;
|
settings: NMPSettings;
|
||||||
|
|
||||||
constructor(app: App, plugin: NoteToMpPlugin) {
|
constructor(app: App, plugin: Note2AnyPlugin) {
|
||||||
super(app, plugin);
|
super(app, plugin);
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.settings = NMPSettings.getInstance();
|
this.settings = NMPSettings.getInstance();
|
||||||
@@ -160,99 +160,141 @@ export class NoteToMpSettingTab extends PluginSettingTab {
|
|||||||
|
|
||||||
this.wxInfo = this.parseWXInfo();
|
this.wxInfo = this.parseWXInfo();
|
||||||
|
|
||||||
const helpEl = containerEl.createEl('div', { cls: 'setting-help-section' });
|
const tabs = [
|
||||||
helpEl.createEl('h2', {text: '帮助文档', cls: 'setting-help-title'});
|
{ id: 'style', label: '样式', render: (panel: HTMLElement) => this.renderStyleTab(panel) },
|
||||||
helpEl.createEl('a', {text: 'https://sunboshi.tech/doc', attr: {href: 'https://sunboshi.tech/doc'}});
|
{ 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('默认样式')
|
.setName('默认样式')
|
||||||
.addDropdown(dropdown => {
|
.addDropdown(dropdown => {
|
||||||
const styles = this.plugin.assetsManager.themes;
|
const styles = this.plugin.assetsManager.themes;
|
||||||
for (let s of styles) {
|
for (const s of styles) {
|
||||||
dropdown.addOption(s.className, s.name);
|
dropdown.addOption(s.className, s.name);
|
||||||
}
|
}
|
||||||
dropdown.setValue(this.settings.defaultStyle);
|
dropdown.setValue(this.settings.defaultStyle);
|
||||||
dropdown.onChange(async (value) => {
|
dropdown.onChange(async (value) => {
|
||||||
this.settings.defaultStyle = value;
|
this.settings.defaultStyle = value;
|
||||||
await this.plugin.saveSettings();
|
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('链接展示样式')
|
.setName('链接展示样式')
|
||||||
.addDropdown(dropdown => {
|
.addDropdown(dropdown => {
|
||||||
dropdown.addOption('inline', '内嵌');
|
dropdown.addOption('inline', '内嵌');
|
||||||
dropdown.addOption('footnote', '脚注');
|
dropdown.addOption('footnote', '脚注');
|
||||||
dropdown.setValue(this.settings.linkStyle);
|
dropdown.setValue(this.settings.linkStyle);
|
||||||
dropdown.onChange(async (value) => {
|
dropdown.onChange(async (value) => {
|
||||||
this.settings.linkStyle = value;
|
this.settings.linkStyle = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(panel)
|
||||||
.setName('文件嵌入展示样式')
|
.setName('文件嵌入展示样式')
|
||||||
.addDropdown(dropdown => {
|
.addDropdown(dropdown => {
|
||||||
dropdown.addOption('quote', '引用');
|
dropdown.addOption('quote', '引用');
|
||||||
dropdown.addOption('content', '正文');
|
dropdown.addOption('content', '正文');
|
||||||
dropdown.setValue(this.settings.embedStyle);
|
dropdown.setValue(this.settings.embedStyle);
|
||||||
dropdown.onChange(async (value) => {
|
dropdown.onChange(async (value) => {
|
||||||
this.settings.embedStyle = value;
|
this.settings.embedStyle = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(panel)
|
||||||
.setName('数学公式语法')
|
.setName('数学公式语法')
|
||||||
.addDropdown(dropdown => {
|
.addDropdown(dropdown => {
|
||||||
dropdown.addOption('latex', 'latex');
|
dropdown.addOption('latex', 'latex');
|
||||||
dropdown.addOption('asciimath', 'asciimath');
|
dropdown.addOption('asciimath', 'asciimath');
|
||||||
dropdown.setValue(this.settings.math);
|
dropdown.setValue(this.settings.math);
|
||||||
dropdown.onChange(async (value) => {
|
dropdown.onChange(async (value) => {
|
||||||
this.settings.math = value;
|
this.settings.math = value;
|
||||||
cleanMathCache();
|
cleanMathCache();
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(panel)
|
||||||
.setName('显示代码行号')
|
.setName('显示代码行号')
|
||||||
.addToggle(toggle => {
|
.addToggle(toggle => {
|
||||||
toggle.setValue(this.settings.lineNumber);
|
toggle.setValue(this.settings.lineNumber);
|
||||||
toggle.onChange(async (value) => {
|
toggle.onChange(async (value) => {
|
||||||
this.settings.lineNumber = value;
|
this.settings.lineNumber = value;
|
||||||
await this.plugin.saveSettings();
|
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 根路径')
|
.setName('Gallery 根路径')
|
||||||
.setDesc('用于 {{<gallery dir="..."/>}} 短代码解析;需指向本地图片根目录')
|
.setDesc('用于 {{<gallery dir="..."/>}} 短代码解析;需指向本地图片根目录')
|
||||||
.addText(text => {
|
.addText(text => {
|
||||||
@@ -265,7 +307,7 @@ export class NoteToMpSettingTab extends PluginSettingTab {
|
|||||||
text.inputEl.setAttr('style', 'width: 360px;');
|
text.inputEl.setAttr('style', 'width: 360px;');
|
||||||
});
|
});
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(panel)
|
||||||
.setName('Gallery 选取图片数')
|
.setName('Gallery 选取图片数')
|
||||||
.setDesc('每个 gallery 短代码最多替换为前 N 张图片')
|
.setDesc('每个 gallery 短代码最多替换为前 N 张图片')
|
||||||
.addText(text => {
|
.addText(text => {
|
||||||
@@ -281,7 +323,18 @@ export class NoteToMpSettingTab extends PluginSettingTab {
|
|||||||
text.inputEl.setAttr('style', 'width: 120px;');
|
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('默认封面图片')
|
.setName('默认封面图片')
|
||||||
.setDesc('当文章无任何图片/短代码时使用;可填 wikilink 文件名或 http(s) URL')
|
.setDesc('当文章无任何图片/短代码时使用;可填 wikilink 文件名或 http(s) URL')
|
||||||
.addText(text => {
|
.addText(text => {
|
||||||
@@ -293,35 +346,86 @@ export class NoteToMpSettingTab extends PluginSettingTab {
|
|||||||
});
|
});
|
||||||
text.inputEl.setAttr('style', 'width: 360px;');
|
text.inputEl.setAttr('style', 'width: 360px;');
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
new Setting(containerEl)
|
private renderThemeTab(panel: HTMLElement): void {
|
||||||
.setName('忽略 frontmatter 封面')
|
new Setting(panel)
|
||||||
.setDesc('开启后不使用 frontmatter 中 cover/image 字段,封面将按正文首图→gallery→默认封面回退')
|
.setName('获取更多主题')
|
||||||
.addToggle(toggle => {
|
.addButton(button => {
|
||||||
toggle.setValue(this.settings.ignoreFrontmatterImage);
|
button.setButtonText('下载');
|
||||||
toggle.onChange(async (value) => {
|
button.onClick(async () => {
|
||||||
this.settings.ignoreFrontmatterImage = value;
|
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();
|
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)
|
const customCSSDoc = '使用指南:<a href="https://sunboshi.tech/customcss">https://sunboshi.tech/customcss</a>';
|
||||||
.setName('启用空行渲染')
|
new Setting(panel)
|
||||||
.addToggle(toggle => {
|
.setName('自定义CSS笔记')
|
||||||
toggle.setValue(this.settings.enableEmptyLine);
|
.setDesc(sanitizeHTMLToDom(customCSSDoc))
|
||||||
toggle.onChange(async (value) => {
|
.addText(text => {
|
||||||
this.settings.enableEmptyLine = value;
|
text.setPlaceholder('请输入自定义CSS笔记标题')
|
||||||
await this.plugin.saveSettings();
|
.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>';
|
||||||
containerEl.createEl('h2', {text: '切图配置'});
|
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('切图保存路径')
|
.setName('切图保存路径')
|
||||||
.setDesc('切图文件的保存目录,默认:/Users/gavin/note2mp/images/xhs')
|
.setDesc('切图文件的保存目录,默认:/Users/gavin/note2any/images/xhs')
|
||||||
.addText(text => {
|
.addText(text => {
|
||||||
text.setPlaceholder('例如 /Users/xxx/images/xhs')
|
text.setPlaceholder('例如 /Users/xxx/images/xhs')
|
||||||
.setValue(this.settings.sliceImageSavePath || '')
|
.setValue(this.settings.sliceImageSavePath || '')
|
||||||
@@ -332,7 +436,7 @@ export class NoteToMpSettingTab extends PluginSettingTab {
|
|||||||
text.inputEl.setAttr('style', 'width: 360px;');
|
text.inputEl.setAttr('style', 'width: 360px;');
|
||||||
});
|
});
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(panel)
|
||||||
.setName('切图宽度')
|
.setName('切图宽度')
|
||||||
.setDesc('长图及切图的宽度(像素),默认:1080')
|
.setDesc('长图及切图的宽度(像素),默认:1080')
|
||||||
.addText(text => {
|
.addText(text => {
|
||||||
@@ -348,7 +452,7 @@ export class NoteToMpSettingTab extends PluginSettingTab {
|
|||||||
text.inputEl.setAttr('style', 'width: 120px;');
|
text.inputEl.setAttr('style', 'width: 120px;');
|
||||||
});
|
});
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(panel)
|
||||||
.setName('切图横竖比例')
|
.setName('切图横竖比例')
|
||||||
.setDesc('格式:宽:高,例如 3:4 表示竖图,16:9 表示横图')
|
.setDesc('格式:宽:高,例如 3:4 表示竖图,16:9 表示横图')
|
||||||
.addText(text => {
|
.addText(text => {
|
||||||
@@ -361,181 +465,118 @@ export class NoteToMpSettingTab extends PluginSettingTab {
|
|||||||
text.inputEl.setAttr('style', 'width: 120px;');
|
text.inputEl.setAttr('style', 'width: 120px;');
|
||||||
});
|
});
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(panel)
|
||||||
.setName('渲染图片标题')
|
.setName('渲染图片标题')
|
||||||
.addToggle(toggle => {
|
.addToggle(toggle => {
|
||||||
toggle.setValue(this.settings.useFigcaption);
|
toggle.setValue(this.settings.useFigcaption);
|
||||||
toggle.onChange(async (value) => {
|
toggle.onChange(async (value) => {
|
||||||
this.settings.useFigcaption = value;
|
this.settings.useFigcaption = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
})
|
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(panel)
|
||||||
.setName('Excalidraw 渲染为 PNG 图片')
|
.setName('Excalidraw 渲染为 PNG 图片')
|
||||||
.setDesc('开启:将 Excalidraw 笔记/嵌入转换为位图 PNG 插入;关闭:保持原始 SVG/矢量渲染(更清晰,体积更小)。')
|
.setDesc('开启:将 Excalidraw 笔记/嵌入转换为位图 PNG 插入;关闭:保持原始 SVG/矢量渲染。')
|
||||||
.addToggle(toggle => {
|
.addToggle(toggle => {
|
||||||
toggle.setValue(this.settings.excalidrawToPNG);
|
toggle.setValue(this.settings.excalidrawToPNG);
|
||||||
toggle.onChange(async (value) => {
|
toggle.onChange(async (value) => {
|
||||||
this.settings.excalidrawToPNG = value;
|
this.settings.excalidrawToPNG = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
})
|
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(panel)
|
||||||
.setName('水印图片')
|
.setName('水印图片')
|
||||||
|
.setDesc('可填写文件名或 URL')
|
||||||
.addText(text => {
|
.addText(text => {
|
||||||
text.setPlaceholder('请输入图片名称')
|
text.setPlaceholder('例如 watermark.png')
|
||||||
.setValue(this.settings.watermark)
|
.setValue(this.settings.watermark)
|
||||||
.onChange(async (value) => {
|
.onChange(async (value) => {
|
||||||
this.settings.watermark = value.trim();
|
this.settings.watermark = value.trim();
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
.inputEl.setAttr('style', 'width: 320px;')
|
.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();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
new Setting(containerEl)
|
private renderUserTab(panel: HTMLElement): void {
|
||||||
.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;')
|
|
||||||
});
|
|
||||||
|
|
||||||
let descHtml = '详情说明:<a href="https://sunboshi.tech/subscribe">https://sunboshi.tech/subscribe</a>';
|
let descHtml = '详情说明:<a href="https://sunboshi.tech/subscribe">https://sunboshi.tech/subscribe</a>';
|
||||||
if (this.settings.isVip) {
|
if (this.settings.isVip) {
|
||||||
descHtml = '<span style="color:rgb(245, 70, 85);font-weight: bold;">👑永久会员</span><br/>' + descHtml;
|
descHtml = '<span style="color:rgb(245, 70, 85);font-weight: bold;">👑永久会员</span><br/>' + descHtml;
|
||||||
}
|
}
|
||||||
else if (this.settings.expireat) {
|
else if (this.settings.expireat) {
|
||||||
const timestr = this.settings.expireat.toLocaleString();
|
const timestr = this.settings.expireat.toLocaleString();
|
||||||
descHtml = `有效期至:${timestr} <br/>${descHtml}`
|
descHtml = `有效期至:${timestr} <br/>${descHtml}`;
|
||||||
}
|
}
|
||||||
new Setting(containerEl)
|
|
||||||
|
new Setting(panel)
|
||||||
.setName('注册码(AuthKey)')
|
.setName('注册码(AuthKey)')
|
||||||
.setDesc(sanitizeHTMLToDom(descHtml))
|
.setDesc(sanitizeHTMLToDom(descHtml))
|
||||||
.addText(text => {
|
.addText(text => {
|
||||||
text.setPlaceholder('请输入注册码')
|
text.setPlaceholder('请输入注册码')
|
||||||
.setValue(this.settings.authKey)
|
.setValue(this.settings.authKey)
|
||||||
.onChange(async (value) => {
|
.onChange(async (value) => {
|
||||||
this.settings.authKey = value.trim();
|
this.settings.authKey = value.trim();
|
||||||
this.settings.getExpiredDate();
|
this.settings.getExpiredDate();
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
.inputEl.setAttr('style', 'width: 320px;')
|
.inputEl.setAttr('style', 'width: 320px;');
|
||||||
}).descEl.setAttr('style', '-webkit-user-select: text; user-select: text;')
|
}).descEl.setAttr('style', '-webkit-user-select: text; user-select: text;');
|
||||||
|
|
||||||
|
|
||||||
let isClear = this.settings.wxInfo.length > 0;
|
let isClear = this.settings.wxInfo.length > 0;
|
||||||
let isRealClear = false;
|
let isRealClear = false;
|
||||||
const buttonText = isClear ? '清空公众号信息' : '保存公众号信息';
|
const buttonText = isClear ? '清空公众号信息' : '保存公众号信息';
|
||||||
new Setting(containerEl)
|
|
||||||
|
new Setting(panel)
|
||||||
.setName('公众号信息')
|
.setName('公众号信息')
|
||||||
.addTextArea(text => {
|
.addTextArea(text => {
|
||||||
this.wxTextArea = text;
|
this.wxTextArea = text;
|
||||||
text.setPlaceholder('请输入公众号信息\n格式:公众号名称|公众号AppID|公众号AppSecret\n多个公众号请换行输入\n输入完成后点击加密按钮')
|
text.setPlaceholder('请输入公众号信息\n格式:公众号名称|公众号AppID|公众号AppSecret\n多个公众号请换行输入\n输入完成后点击加密按钮')
|
||||||
.setValue(this.wxInfo)
|
.setValue(this.wxInfo)
|
||||||
.onChange(value => {
|
.onChange(value => {
|
||||||
this.wxInfo = value;
|
this.wxInfo = value;
|
||||||
})
|
})
|
||||||
.inputEl.setAttr('style', 'width: 520px; height: 120px;');
|
.inputEl.setAttr('style', 'width: 520px; height: 120px;');
|
||||||
})
|
})
|
||||||
|
.addButton(button => {
|
||||||
new Setting(containerEl).addButton(button => {
|
button.setButtonText(buttonText);
|
||||||
button.setButtonText(buttonText);
|
button.onClick(async () => {
|
||||||
button.onClick(async () => {
|
if (isClear) {
|
||||||
if (isClear) {
|
isRealClear = true;
|
||||||
isRealClear = true;
|
isClear = false;
|
||||||
isClear = false;
|
button.setButtonText('确认清空?');
|
||||||
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('清空公众号信息');
|
|
||||||
}
|
}
|
||||||
else {
|
else if (isRealClear) {
|
||||||
|
isRealClear = false;
|
||||||
|
isClear = false;
|
||||||
|
this.clear();
|
||||||
button.setButtonText('保存公众号信息');
|
button.setButtonText('保存公众号信息');
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
});
|
button.setButtonText('保存中...');
|
||||||
})
|
if (await this.encrypt()) {
|
||||||
.addButton(button => {
|
isClear = true;
|
||||||
button.setButtonText('测试公众号');
|
isRealClear = false;
|
||||||
button.onClick(async () => {
|
button.setButtonText('清空公众号信息');
|
||||||
button.setButtonText('测试中...');
|
}
|
||||||
await this.testWXInfo();
|
else {
|
||||||
button.setButtonText('测试公众号');
|
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;
|
excalidrawToPNG: boolean;
|
||||||
isLoaded: boolean = false;
|
isLoaded: boolean = false;
|
||||||
enableEmptyLine: boolean = false;
|
enableEmptyLine: boolean = false;
|
||||||
|
needOpenComment: boolean = true;
|
||||||
enableMarkdownImageToWikilink: boolean = true; // 自动将  转为 ![[file.ext]]
|
enableMarkdownImageToWikilink: boolean = true; // 自动将  转为 ![[file.ext]]
|
||||||
// gallery 相关配置:根目录前缀 & 选取图片数量
|
// gallery 相关配置:根目录前缀 & 选取图片数量
|
||||||
galleryPrePath: string;
|
galleryPrePath: string;
|
||||||
@@ -81,6 +82,7 @@ export class NMPSettings {
|
|||||||
this.excalidrawToPNG = false;
|
this.excalidrawToPNG = false;
|
||||||
this.expertSettingsNote = '';
|
this.expertSettingsNote = '';
|
||||||
this.enableEmptyLine = false;
|
this.enableEmptyLine = false;
|
||||||
|
this.needOpenComment = true;
|
||||||
this.enableMarkdownImageToWikilink = true;
|
this.enableMarkdownImageToWikilink = true;
|
||||||
// 默认值:用户原先硬编码路径 & 前 2 张
|
// 默认值:用户原先硬编码路径 & 前 2 张
|
||||||
this.galleryPrePath = '/Users/gavin/myweb/static';
|
this.galleryPrePath = '/Users/gavin/myweb/static';
|
||||||
@@ -98,7 +100,7 @@ export class NMPSettings {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
// 切图配置默认值
|
// 切图配置默认值
|
||||||
this.sliceImageSavePath = '/Users/gavin/note2mp/images/xhs';
|
this.sliceImageSavePath = '/Users/gavin/note2any/images/xhs';
|
||||||
this.sliceImageWidth = 1080;
|
this.sliceImageWidth = 1080;
|
||||||
this.sliceImageAspectRatio = '3:4';
|
this.sliceImageAspectRatio = '3:4';
|
||||||
this.xhsPreviewWidth = 540;
|
this.xhsPreviewWidth = 540;
|
||||||
@@ -131,6 +133,7 @@ export class NMPSettings {
|
|||||||
expertSettingsNote,
|
expertSettingsNote,
|
||||||
ignoreEmptyLine,
|
ignoreEmptyLine,
|
||||||
enableMarkdownImageToWikilink,
|
enableMarkdownImageToWikilink,
|
||||||
|
needOpenComment,
|
||||||
galleryPrePath,
|
galleryPrePath,
|
||||||
galleryNumPic,
|
galleryNumPic,
|
||||||
defaultCoverPic,
|
defaultCoverPic,
|
||||||
@@ -160,6 +163,7 @@ export class NMPSettings {
|
|||||||
if (excalidrawToPNG !== undefined) settings.excalidrawToPNG = excalidrawToPNG;
|
if (excalidrawToPNG !== undefined) settings.excalidrawToPNG = excalidrawToPNG;
|
||||||
if (expertSettingsNote) settings.expertSettingsNote = expertSettingsNote;
|
if (expertSettingsNote) settings.expertSettingsNote = expertSettingsNote;
|
||||||
if (ignoreEmptyLine !== undefined) settings.enableEmptyLine = !!ignoreEmptyLine;
|
if (ignoreEmptyLine !== undefined) settings.enableEmptyLine = !!ignoreEmptyLine;
|
||||||
|
if (needOpenComment !== undefined) settings.needOpenComment = !!needOpenComment;
|
||||||
if (enableMarkdownImageToWikilink !== undefined) settings.enableMarkdownImageToWikilink = !!enableMarkdownImageToWikilink;
|
if (enableMarkdownImageToWikilink !== undefined) settings.enableMarkdownImageToWikilink = !!enableMarkdownImageToWikilink;
|
||||||
if (galleryPrePath) settings.galleryPrePath = galleryPrePath;
|
if (galleryPrePath) settings.galleryPrePath = galleryPrePath;
|
||||||
if (galleryNumPic !== undefined && Number.isFinite(galleryNumPic)) settings.galleryNumPic = Math.max(1, parseInt(galleryNumPic));
|
if (galleryNumPic !== undefined && Number.isFinite(galleryNumPic)) settings.galleryNumPic = Math.max(1, parseInt(galleryNumPic));
|
||||||
@@ -197,6 +201,7 @@ export class NMPSettings {
|
|||||||
'excalidrawToPNG': settings.excalidrawToPNG,
|
'excalidrawToPNG': settings.excalidrawToPNG,
|
||||||
'expertSettingsNote': settings.expertSettingsNote,
|
'expertSettingsNote': settings.expertSettingsNote,
|
||||||
'ignoreEmptyLine': settings.enableEmptyLine,
|
'ignoreEmptyLine': settings.enableEmptyLine,
|
||||||
|
'needOpenComment': settings.needOpenComment,
|
||||||
'enableMarkdownImageToWikilink': settings.enableMarkdownImageToWikilink,
|
'enableMarkdownImageToWikilink': settings.enableMarkdownImageToWikilink,
|
||||||
'galleryPrePath': settings.galleryPrePath,
|
'galleryPrePath': settings.galleryPrePath,
|
||||||
'galleryNumPic': settings.galleryNumPic,
|
'galleryNumPic': settings.galleryNumPic,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function setVersion(version: string) {
|
|||||||
function getStyleSheet() {
|
function getStyleSheet() {
|
||||||
for (var i = 0; i < document.styleSheets.length; i++) {
|
for (var i = 0; i < document.styleSheets.length; i++) {
|
||||||
var sheet = document.styleSheets[i];
|
var sheet = document.styleSheets[i];
|
||||||
if (sheet.title == 'note-to-mp-style') {
|
if (sheet.title == 'note2any-style') {
|
||||||
return sheet;
|
return sheet;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ export class XiaohongshuPreview {
|
|||||||
this.articleHTML = articleHTML;
|
this.articleHTML = articleHTML;
|
||||||
this.currentFile = file;
|
this.currentFile = file;
|
||||||
|
|
||||||
new Notice('正在分页...');
|
//new Notice('正在分页...');
|
||||||
|
|
||||||
// 创建临时容器用于分页
|
// 创建临时容器用于分页
|
||||||
const tempContainer = document.createElement('div');
|
const tempContainer = document.createElement('div');
|
||||||
@@ -200,7 +200,7 @@ export class XiaohongshuPreview {
|
|||||||
// 在分页前先应用主题与高亮,确保测量使用正确样式
|
// 在分页前先应用主题与高亮,确保测量使用正确样式
|
||||||
this.applyThemeCSS();
|
this.applyThemeCSS();
|
||||||
this.pages = await paginateArticle(tempContainer, this.settings);
|
this.pages = await paginateArticle(tempContainer, this.settings);
|
||||||
new Notice(`分页完成:共 ${this.pages.length} 页`);
|
//new Notice(`分页完成:共 ${this.pages.length} 页`);
|
||||||
|
|
||||||
this.currentPageIndex = 0;
|
this.currentPageIndex = 0;
|
||||||
// 初次渲染时应用当前主题
|
// 初次渲染时应用当前主题
|
||||||
@@ -460,7 +460,7 @@ export class XiaohongshuPreview {
|
|||||||
|
|
||||||
private async persistSettings(): Promise<void> {
|
private async persistSettings(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const plugin = (this.app as any)?.plugins?.getPlugin?.('note-to-mp');
|
const plugin = (this.app as any)?.plugins?.getPlugin?.('note2any');
|
||||||
if (plugin?.saveSettings) {
|
if (plugin?.saveSettings) {
|
||||||
await plugin.saveSettings();
|
await plugin.saveSettings();
|
||||||
}
|
}
|
||||||
@@ -520,7 +520,7 @@ export class XiaohongshuPreview {
|
|||||||
if (!this.articleHTML) return;
|
if (!this.articleHTML) return;
|
||||||
const totalBefore = this.pages.length || 1;
|
const totalBefore = this.pages.length || 1;
|
||||||
const posRatio = (this.currentPageIndex + 0.5) / totalBefore; // 以当前页中心作为相对位置
|
const posRatio = (this.currentPageIndex + 0.5) / totalBefore; // 以当前页中心作为相对位置
|
||||||
new Notice('重新分页中...');
|
//new Notice('重新分页中...');
|
||||||
|
|
||||||
const tempContainer = document.createElement('div');
|
const tempContainer = document.createElement('div');
|
||||||
tempContainer.innerHTML = this.articleHTML;
|
tempContainer.innerHTML = this.articleHTML;
|
||||||
@@ -539,7 +539,7 @@ export class XiaohongshuPreview {
|
|||||||
this.currentPageIndex = 0;
|
this.currentPageIndex = 0;
|
||||||
}
|
}
|
||||||
this.renderCurrentPage();
|
this.renderCurrentPage();
|
||||||
new Notice(`重新分页完成:共 ${this.pages.length} 页`);
|
//new Notice(`重新分页完成:共 ${this.pages.length} 页`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('重新分页失败', e);
|
console.error('重新分页失败', e);
|
||||||
new Notice('重新分页失败');
|
new Notice('重新分页失败');
|
||||||
|
|||||||
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);
|
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 的组合声明 */
|
/* focus 规则见与 .upload-input:focus 的组合声明 */
|
||||||
|
|
||||||
.msg-view {
|
.msg-view {
|
||||||
@@ -351,10 +398,10 @@ label:hover { color: var(--c-primary); }
|
|||||||
"cover-select cover-select cover-select cover-select cover-input cover-input"
|
"cover-select cover-select cover-select cover-select cover-input cover-input"
|
||||||
"style-label style-select highlight-label highlight-select highlight-select highlight-select"
|
"style-label style-select highlight-label highlight-select highlight-select highlight-select"
|
||||||
"content content content content content content";
|
"content content content content content content";
|
||||||
gap: 5px;
|
gap: 12px;
|
||||||
background: var(--grad-toolbar);
|
background: var(--grad-toolbar);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
padding: 5px;
|
padding: 16px;
|
||||||
box-shadow: var(--shadow-sm);
|
box-shadow: var(--shadow-sm);
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
37
todolist.md
37
todolist.md
@@ -8,7 +8,7 @@
|
|||||||
✅
|
✅
|
||||||
- 标题取frontmatter的title属性。
|
- 标题取frontmatter的title属性。
|
||||||
✅
|
✅
|
||||||
- 图片保存路径可配置,默认为/Users/gavin/note2mp/images/xhs。
|
- 图片保存路径可配置,默认为/Users/gavin/note2any/images/xhs。
|
||||||
✅
|
✅
|
||||||
- 图片名取frontmatter的slug属性,如: slug: mmm,文章长图命名为mmm.png,如切为3张图片,则切图图片名按顺序依次为mmm_1.png,mmm_2.png,mmm_3.png
|
- 图片名取frontmatter的slug属性,如: slug: mmm,文章长图命名为mmm.png,如切为3张图片,则切图图片名按顺序依次为mmm_1.png,mmm_2.png,mmm_3.png
|
||||||
✅
|
✅
|
||||||
@@ -56,6 +56,28 @@
|
|||||||
- 点击"全部页切图",把所有html页面转为png图片,图片保存路径和命名按此前设置。
|
- 点击"全部页切图",把所有html页面转为png图片,图片保存路径和命名按此前设置。
|
||||||
✅
|
✅
|
||||||
|
|
||||||
|
3. 目前xhs模式发布功能未实现,因为没有登录。
|
||||||
|
整合xhs登陆和发布功能。
|
||||||
|
- 在“用户配置”tab中增加“小红书用户名”配置项,默认13357108011。(⚠️ **后续可能需要维护多账号**)
|
||||||
|
- xhslogin项目,XHS_PHONE=13357108011 XHS_AUTO_CLOSE=1 npm run publish -- --title "恢复功能测试" --content "验证恢复核心功能后保留优化的代码" --images "/Users/gavin/myweb/static/img/ibook笔记.png" --visibility private --collection 自嗨 --timestamp
|
||||||
|
其中:
|
||||||
|
- 参数XHS_PHONE为“小红书用户名”配置项。
|
||||||
|
- title取markdown文章的title
|
||||||
|
- content取markdown文章frontmatter中的description信息,如果没有description或内容为空,取markdown正文前2段(段以空行隔离,不超过300字[n];段1+段2>n,取段1;段1+段2+段3>n,取段1段2 ……)
|
||||||
|
- images取小红书切图结果,按文章次序。
|
||||||
|
- visibility在小红书模式下,预览页的工具栏中增加"公开"checkbox,默认为不选,即private。
|
||||||
|
- collection在小红书模式下,预览页的工具栏中增加下拉框选择,下拉框内容从登录小红书自动获取。
|
||||||
|
- timestamp是调试使用,默认使用
|
||||||
|
|
||||||
|
❓ 重构后无法运行,原因是obsidian中playwright环境没有。
|
||||||
|
❌ 要用xhslogin 的 Playwright 逻辑,就必须把 Playwright 当成外部依赖加载(运行前安装 playwright 和浏览器内核)。 如果一定要彻底摆脱 Playwright,只能回到 webview 方案,重新实现一次自动化流程,与其说“利用现成 Chromium”,不如说“自己写网页自动化框架”,工作量很大。
|
||||||
|
|
||||||
|
**改变思路**:
|
||||||
|
xhslogin改造成MCP server部署在远端服务器上。插件中只要实现mcp client就可以了。
|
||||||
|
这样xhslogin项目没有必要整合到note2any中。
|
||||||
|
|
||||||
|
4. 选中内容,右键发布到不同平台。发布时调用渲染,发布到特定的栏目中,比如网站中某个区块的“闪念集”。
|
||||||
|
|
||||||
## 问题
|
## 问题
|
||||||
1. "发布平台"首次选“小红书”时,预览页面没有加载当前文章。
|
1. "发布平台"首次选“小红书”时,预览页面没有加载当前文章。
|
||||||
✅
|
✅
|
||||||
@@ -118,12 +140,25 @@ SOLVE:obsidian控制台打印信息,定位在哪里阻塞,AI修复。
|
|||||||
10. 新建docs文件夹,把除了README和todolist以外的markdown文件放到docs中。
|
10. 新建docs文件夹,把除了README和todolist以外的markdown文件放到docs中。
|
||||||
✅
|
✅
|
||||||
|
|
||||||
|
11. `` -- 不应该渲染为图片链接
|
||||||
|
|
||||||
|
|
||||||
## 经验
|
## 经验
|
||||||
1. 在不确定AI是否理解,或者需求是否准确的情况下,先用codex chat模式提问,看回答确定AI理解是否准确。
|
1. 在不确定AI是否理解,或者需求是否准确的情况下,先用codex chat模式提问,看回答确定AI理解是否准确。
|
||||||
尤其对于较大规模的重构需求,这点很重要 ‼️ 。
|
尤其对于较大规模的重构需求,这点很重要 ‼️ 。
|
||||||
|
|
||||||
|
例如:
|
||||||
|
这个思路有问题。我提供命令行只是对参数做说明,这些参数实际上是函数参数。
|
||||||
|
我需要把xhslogin的功能代码无缝的整合进现有项目中,而不是通过命令行传参的方式实现。
|
||||||
|
这么做的同时,保持结构和模块清晰。
|
||||||
|
请再提供思路,我来审核下。
|
||||||
|
|
||||||
2. 复杂页面,codex生成的css可能无比复杂,不便于维护修改。
|
2. 复杂页面,codex生成的css可能无比复杂,不便于维护修改。
|
||||||
自己写布局demo原型,让codex参考布局修改(原来元素美化的css可保留)。
|
自己写布局demo原型,让codex参考布局修改(原来元素美化的css可保留)。
|
||||||
demo原型可以手绘后,拍照让chatgpt生成,在此基础上自己修改。
|
demo原型可以手绘后,拍照让chatgpt生成,在此基础上自己修改。
|
||||||
|
|
||||||
|
## 思路
|
||||||
|
1. 网上图片模版,让AI快速生成css themes主题。快速套用。‼️
|
||||||
|
**样式和功能必须结构,样式必须可以0基础,快速选择,让用户可以选择足够多样式(AI生成)。**
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
4
x
4
x
@@ -105,8 +105,8 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 若内部 section.note-to-mp 主题没有撑开,确保文本可见基色 */
|
/* 若内部 section.note2any 主题没有撑开,确保文本可见基色 */
|
||||||
.wechat-article-wrapper .note-to-mp {
|
.wechat-article-wrapper .note2any {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user