mirror of
https://github.com/jaywcjlove/wxmp.git
synced 2026-01-12 00:08:50 +08:00
feat: support custom style & ignore content syntax.
This commit is contained in:
47
README.md
47
README.md
@@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
微信公众号文章 Markdown 编辑器,使用 markdown 语法创建一篇简介美观大方的微信公众号图文。由于发版本麻烦,和一些功能无法扩展停滞开发了,未来不再开发 Chrome 的工具(暂存在 chrome 分支),通过 web 版本定制更丰富的功能。
|

|
||||||
|
|
||||||
|
微信公众号文章 Markdown 在线编辑器,使用 markdown 语法创建一篇简介美观大方的微信公众号图文。由于发版本麻烦,和一些功能无法扩展停滞开发了,未来不再开发 Chrome 的工具(暂存在 chrome 分支),通过 web 版本定制更丰富的功能。
|
||||||
|
|
||||||
## 功能特性
|
## 功能特性
|
||||||
|
|
||||||
@@ -56,10 +58,11 @@ Inline Code `{code: 0}`
|
|||||||
|
|
||||||
### 支持 GFM 脚注
|
### 支持 GFM 脚注
|
||||||
|
|
||||||
这是一个简单的脚注[^1]。 页面最后有一些额外的文字描述。注意这不是完整的注脚[^2]。
|
这是一个简单的 Markdown[^1] 语法的脚注[^2]。 页面最后有一些额外的文字描述。注意这不是完整的注脚[^3]特性。
|
||||||
|
|
||||||
[^1]: https://github.github.com/gfm/
|
[^1]: GitHub 风格的 Markdown 规范 https://github.github.com/gfm/
|
||||||
[^2]: 微信文章不支持锚点跳转和打开第三方 URL 超链接,所以不支持完整的注脚
|
[^2]: 脚注 https://github.blog/changelog/2021-09-30-footnotes-now-supported-in-markdown-fields/
|
||||||
|
[^3]: 微信文章不支持锚点跳转和打开第三方 URL 超链接,所以不支持完整的注脚特性。
|
||||||
|
|
||||||
### 支持注释
|
### 支持注释
|
||||||
|
|
||||||
@@ -72,6 +75,31 @@ Inline Code `{code: 0}`
|
|||||||
音 <rp></rp><rt>yin</rt><rp></rp>
|
音 <rp></rp><rt>yin</rt><rp></rp>
|
||||||
</ruby>
|
</ruby>
|
||||||
|
|
||||||
|
### 支持自定义样式
|
||||||
|
<!--rehype:style=color: red;-->
|
||||||
|
|
||||||
|
在 Markdown 中 HTML 注释也可以用在 markdown 中,利用这一特点,为一些内容自定一样式。使用 HTML 注释 `<!--rehype:xxx-->`<!--rehype:style=color: red;background: #ff000033;--> 让 Markdown 支持样式自定义。
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## 定义标题样式
|
||||||
|
<!--rehype:style=display: flex; height: 230px; align-items: center; justify-content: center; font-size: 38px;-->
|
||||||
|
|
||||||
|
支持对某些文字变更样式,如_文字颜色_<!--rehype:style=color: red;-->,文字颜色将被设置为红色(red)。
|
||||||
|
```
|
||||||
|
|
||||||
|
⚠️ 注意:这一特性可能适用于有一定 css 前端基础知识的用户,不过它也非常简单,使用 `<!--rehype:style=` 开始,`-->` 结束,中间包裹 css 样式,如 `color: red;` 设置文字红色。
|
||||||
|
|
||||||
|
|
||||||
|
### 标记忽略内容
|
||||||
|
|
||||||
|
此特性利用 HTML 注释在 markdown 中被忽略的特性,标记需要忽略的内容,标记开始 `<!--rehype:ignore:start-->`,标记结束 `<!--rehype:ignore:end-->`,被标记的内容在微信 Markdown 编辑器预览中不显示。在其它预览工具中展示内容,比如 GitHub 中能展示。
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# 注释忽略
|
||||||
|
|
||||||
|
<!--rehype:ignore:start-->内容在微信 Markdown 编辑器预览中不显示。在其它预览工具中展示内容。<!--rehype:ignore:end-->
|
||||||
|
```
|
||||||
|
|
||||||
## 部署
|
## 部署
|
||||||
|
|
||||||
[](https://hub.docker.com/r/wcjiang/wxmp) [](https://hub.docker.com/r/wcjiang/wxmp) [](https://hub.docker.com/r/wcjiang/wxmp)
|
[](https://hub.docker.com/r/wcjiang/wxmp) [](https://hub.docker.com/r/wcjiang/wxmp) [](https://hub.docker.com/r/wcjiang/wxmp)
|
||||||
@@ -98,16 +126,19 @@ docker run --name wxmp -itd -p 96611:3000 ghcr.io/jaywcjlove/wxmp:latest
|
|||||||
http://localhost:96611/
|
http://localhost:96611/
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contributors
|
## 贡献者
|
||||||
|
|
||||||
As always, thanks to our amazing contributors!
|
一如既往,感谢我们出色的贡献者!
|
||||||
|
|
||||||
<a href="https://github.com/jaywcjlove/wxmp/graphs/contributors">
|
<a href="https://github.com/jaywcjlove/wxmp/graphs/contributors">
|
||||||
<img src="https://jaywcjlove.github.io/wxmp/CONTRIBUTORS.svg" />
|
<img src="https://jaywcjlove.github.io/wxmp/CONTRIBUTORS.svg" />
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
Made with [github-action-contributors](https://github.com/jaywcjlove/github-action-contributors).
|
上图贡献者列表,由 [action-contributors](https://github.com/jaywcjlove/github-action-contributors)[^4] 自动生成贡献者图片。
|
||||||
|
|
||||||
|
|
||||||
|
[^4]: Action Contributors https://github.com/jaywcjlove/github-action-contributors
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed under the MIT License.
|
根据 MIT 许可证获得许可。
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hot-toast": "^2.3.0",
|
"react-hot-toast": "^2.3.0",
|
||||||
"react-router-dom": "^6.3.0",
|
"react-router-dom": "^6.3.0",
|
||||||
|
"rehype-attr": "^2.0.8",
|
||||||
|
"rehype-ignore": "^1.0.1",
|
||||||
"rehype-prism-plus": "^1.5.0",
|
"rehype-prism-plus": "^1.5.0",
|
||||||
"rehype-raw": "^6.1.1",
|
"rehype-raw": "^6.1.1",
|
||||||
"rehype-stringify": "^9.0.3",
|
"rehype-stringify": "^9.0.3",
|
||||||
|
|||||||
@@ -163,6 +163,21 @@ th {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-warpper {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 0rem;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
display: initial;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.comment { color: #6a737d; }
|
.comment { color: #6a737d; }
|
||||||
.property { color: #6f42c1; }
|
.property { color: #6f42c1; }
|
||||||
.function { color: #6f42c1; }
|
.function { color: #6f42c1; }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { RootContent, Element, Text } from 'hast';
|
import { RootContent, Element, Text, Root } from 'hast';
|
||||||
|
|
||||||
export const getBlock = (data: any, str: string = '') => {
|
export const getBlock = (data: any, str: string = '') => {
|
||||||
if (data && data.data && data.data.type === 'Declaration') {
|
if (data && data.data && data.data.type === 'Declaration') {
|
||||||
@@ -88,3 +88,13 @@ export const footnotesLabel = (node: Element) => {
|
|||||||
value: `[${label}]`
|
value: `[${label}]`
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const imagesStyle = (node: Element, parent: Root | Element | null) => {
|
||||||
|
if (parent?.type === 'element' && parent.tagName === 'p' && node?.type === 'element' && node.tagName === 'img') {
|
||||||
|
parent.tagName = 'figure';
|
||||||
|
if (!parent.properties) parent.properties = {}
|
||||||
|
parent.properties.className = ['image-warpper']
|
||||||
|
if (!node.properties) node.properties = {}
|
||||||
|
node.properties.className = ['image']
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,9 +7,11 @@ import remarkGfm from 'remark-gfm';
|
|||||||
import remarkRehype from 'remark-rehype';
|
import remarkRehype from 'remark-rehype';
|
||||||
import rehypePrism from 'rehype-prism-plus';
|
import rehypePrism from 'rehype-prism-plus';
|
||||||
import rehypeRaw from 'rehype-raw';
|
import rehypeRaw from 'rehype-raw';
|
||||||
|
import rehypeAttrs from 'rehype-attr';
|
||||||
|
import rehypeIgnore from 'rehype-ignore';
|
||||||
import rehypeRewrite from 'rehype-rewrite';
|
import rehypeRewrite from 'rehype-rewrite';
|
||||||
import stringify from 'rehype-stringify';
|
import stringify from 'rehype-stringify';
|
||||||
import { cssdata, spaceEscape, footnotes, footnotesLabel } from './css';
|
import { cssdata, spaceEscape, footnotes, footnotesLabel, imagesStyle } from './css';
|
||||||
|
|
||||||
export type MarkdownToHTMLOptions = {
|
export type MarkdownToHTMLOptions = {
|
||||||
|
|
||||||
@@ -25,14 +27,18 @@ export function markdownToHTML(md: string, css: string, options: MarkdownToHTMLO
|
|||||||
});
|
});
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const data = cssdata(ast.children.head);
|
const data = cssdata(ast.children.head);
|
||||||
|
console.log(data)
|
||||||
const processor = unified()
|
const processor = unified()
|
||||||
.use(remarkParse)
|
.use(remarkParse)
|
||||||
|
.use(remarkGfm)
|
||||||
.use(remarkRehype, { allowDangerousHtml: true })
|
.use(remarkRehype, { allowDangerousHtml: true })
|
||||||
.use(rehypePrism)
|
.use(rehypePrism)
|
||||||
.use(remarkGfm)
|
|
||||||
.use(rehypeRaw)
|
.use(rehypeRaw)
|
||||||
|
.use(rehypeIgnore, { })
|
||||||
|
.use(rehypeAttrs, { properties: 'attr' })
|
||||||
.use(rehypeRewrite, {
|
.use(rehypeRewrite, {
|
||||||
rewrite: (node, index, parent) => {
|
rewrite: (node, index, parent) => {
|
||||||
|
// @ts-ignore
|
||||||
if (node?.type === 'element' && node?.tagName === 'code' && parent?.type === 'element' && parent?.tagName === 'pre') {
|
if (node?.type === 'element' && node?.tagName === 'code' && parent?.type === 'element' && parent?.tagName === 'pre') {
|
||||||
spaceEscape(node)
|
spaceEscape(node)
|
||||||
}
|
}
|
||||||
@@ -42,17 +48,23 @@ export function markdownToHTML(md: string, css: string, options: MarkdownToHTMLO
|
|||||||
if (node?.type === 'element' && node.tagName === 'sup') {
|
if (node?.type === 'element' && node.tagName === 'sup') {
|
||||||
footnotesLabel(node)
|
footnotesLabel(node)
|
||||||
}
|
}
|
||||||
|
if (node?.type === 'element' && node.tagName === 'img') {
|
||||||
|
imagesStyle(node, parent)
|
||||||
|
}
|
||||||
|
// Code Spans style
|
||||||
if (node?.type === 'element' && node?.tagName === 'code' && parent?.type === 'element' && parent?.tagName !== 'pre') {
|
if (node?.type === 'element' && node?.tagName === 'code' && parent?.type === 'element' && parent?.tagName !== 'pre') {
|
||||||
if (!node.properties) node.properties = {}
|
if (!node.properties) node.properties = {}
|
||||||
node.properties!.className = ['code-spans'];
|
node.properties!.className = ['code-spans'];
|
||||||
}
|
}
|
||||||
if (node?.type === 'element') {
|
// List TODO style
|
||||||
if (node.tagName === 'input' && parent?.type === 'element') {
|
if (parent?.type === 'element' && node?.type === 'element' && node?.tagName === 'input') {
|
||||||
if (parent && parent.type === 'element') {
|
if (parent && parent.type === 'element') {
|
||||||
parent.children = parent?.children.filter(elm => (elm as Element).tagName !== 'input')
|
parent.children = parent?.children.filter(elm => (elm as Element).tagName !== 'input')
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Support *.md.css
|
||||||
|
if (node?.type === 'element') {
|
||||||
if (!node.properties) {
|
if (!node.properties) {
|
||||||
node.properties = {};
|
node.properties = {};
|
||||||
}
|
}
|
||||||
@@ -67,7 +79,7 @@ export function markdownToHTML(md: string, css: string, options: MarkdownToHTMLO
|
|||||||
}
|
}
|
||||||
if (!style) style = data[node.tagName];
|
if (!style) style = data[node.tagName];
|
||||||
if (style) {
|
if (style) {
|
||||||
node.properties.style = style;
|
node.properties.style = style + (node.properties.style || '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user