关于 markdown-to-whatsapp
markdown-to-whatsapp 可将 Markdown 转为 WhatsApp 格式,提供网页工具、npm 库、命令行工具和 MCP 服务。表格会按可配置的等宽气泡宽度排版,必要时可改用列表。
主要功能
- 01转换 WhatsApp 格式的 Markdown 样式
- 02按设定的气泡宽度排版表格
- 03选择表格和列表布局
- 04在 WhatsApp 气泡中预览消息
- 05作为库、命令行工具或网页工具使用
适用场景
- 01准备适合在 WhatsApp 发送的格式化笔记
- 02让 Markdown 表格适配手机文本宽度
- 03转换文件或标准输入中的 Markdown
- 04分享前预览消息换行效果
MCP 相关
项目提供供 Agent 使用的 MCP 服务,可执行 Markdown 到 WhatsApp 格式的转换。提供的 README 片段未说明其 MCP 工具或设置方式。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
1收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| markdown | string | 是 | 要转换的 Markdown 文本 |
| monoWidth | integer | 否 | 读者的 WhatsApp 气泡中一行可容纳的等宽字符数;在 360 px 手机上约为 26 个(默认值 26)。表格会调整为适合此宽度。 |
| listLayout | enum | 否 | 表格转换为列表时的显示方式:rows(每行一组)、columns(每列一组,用于比较矩阵)、pairs(仅两列,显示为不带项目符号的“key: value”行)。auto 会根据表头进行判断。默认值为 auto。auto | rows | columns | pairs |
| tableFormat | enum | 否 | auto:适合 monoWidth 时绘制表格,否则转换为项目符号列表。list:始终转换为项目符号列表。默认值为 auto。auto | list |
| rowSeparator | boolean | 否 | 在表格行之间绘制分隔线。行内容换行时始终绘制。默认值为 false。 |
| headingEmojis | boolean | 否 | 在粗体文本前添加级别表情符号(📌 🟠 🟡 …)作为标题前缀。默认值为 true。 |
详细介绍
Markdown 转 WhatsApp 转换器
将标准 Markdown 转换为 WhatsApp 的格式语法——可作为网页、npm 库、命令行工具或供 Agent 使用的 MCP 服务。
npm i markdown-to-whatsapp · npx markdown-to-whatsapp mcp
此工具的用途
WhatsApp 使用一种非标准的文本格式语法(例如 *粗体*、_斜体_、~删除线~)。它与标准 Markdown 相似,但并不完全相同。
此工具提供了一种简单的方法,可将 Markdown 来源(如文本编辑器、Google 文档等)中的文本转换为 WhatsApp 所需的格式,省去手动修正的麻烦。
整个转换过程都通过 JavaScript 在浏览器本地运行,解析器也随页面一起提供。数据绝不会发送到服务器——离开页面的唯一请求是获取字体。
支持的转换
脚本使用 marked 库进行基于 AST 的解析,并支持以下格式:
文本样式
- 粗体:
**text**→*text* - 斜体:
*text*或_text_→_text_ - 删除线:
~~text~~→~text~ - 行内代码:
`code`→`code` - 粗斜体:
***text***→_*text*_(同时保留两种样式)
标题
标题会转换为粗体文本,并根据级别添加对应的表情符号前缀:
# H1→*📌 H1*## H2→*🟠 H2*### H3→*🟡 H3*- 以此类推……
可在界面中关闭表情符号前缀(标题 · 表情符号),仅保留纯文本 *标题*。
列表
- 无序列表: 使用
*前缀,嵌套级别使用◦- 第 1 级:
* Item - 第 2 级:
* ◦ Item - 第 3 级:
* ◦ ◦ Item
- 第 1 级:
- 有序列表: 保留编号,嵌套级别使用
◦标记1. A/◦ 1. A1/◦ ◦ 1. A1a/2. B
- 任务列表:
- [x]→☑,- [ ]→☐,有序列表中也适用(1. ☑ 已完成) - 松散列表项: 同一列表项中的多个段落会合并到一行
- 列表项中的块级内容: 代码块、引用块和嵌套列表会另起一行,显示在列表项下方
气泡宽度
WhatsApp 气泡每行可容纳固定数量的等宽字符——在 360 px 的手机上约为 26 个,这是默认值。给自己发送一个代码块,数一数它在哪个位置换行,即可测出宽度,然后在栏中设置 宽度(旁边的 ? 会显示相同说明)。字段接受 10 到 80 之间的数值:没有手机会超出这个范围。
这个数值取决于手机,而非某张表格,因此适用于所有等宽内容:表格会调整以保持在该宽度以内;预览则会严格按此宽度绘制每个代码块,并在收件人的 WhatsApp 中相同的位置换行。
表格
表格会按以下 两种样式之一呈现,可在界面中为整个文档或单张表格选择:
-
自动(默认):在等宽代码块中绘制表格,宽度按需调整且不超过气泡宽度;无法绘制表格时则显示为项目符号列表。
+--------+-------------+ | Name | Description | +========+=============+ | Value | Details | +--------+-------------+ -
列表:始终显示为项目符号列表。
列表的布局方式
列表可以通过三种方式对单元格分组。转换器会根据表头和粗体单元格猜测布局,也可以针对每张表格覆盖此猜测(布局:自动 · 行 · 列 · 配对):
-
键值对 — 2 列,表头不限:每一行都是一条
key: value。在每一行都写出表头,比起Italy: Rome,几乎在所有表格里读起来都更糟。* *CPU:* Intel Xeon * *RAM:* 64 GB * *Storage:* 1 TB SSD -
按列分组 — 3 列或更多列,且第一列表头为空或是属性名称(“Feature”“Spec”“Parameter”……),或者第一列加粗:这是一个比较矩阵,被比较的对象是各列,因此每列会成为一个分组。
* *Proxmox* * ◦ _Kernel:_ KVM * ◦ _License:_ AGPL v3 * *ESXi* * ◦ _Kernel:_ VMkernel * ◦ _License:_ Proprietary -
按行分组 — 其他所有情况:每行一个分组,标签取自第一个单元格。
* *Product:* Laptop * ◦ _Price:_ $999 * ◦ _Stock:_ 50 * *Product:* Smartphone * ◦ _Price:_ $599 * ◦ _Stock:_ 100
属性词按单词逐一匹配(“Species”不算作“spec”),支持 11 种语言:英语、意大利语、西班牙语、法语、葡萄牙语、德语、俄语、阿拉伯语、印地语、孟加拉语和印度尼西亚语。键值对必须恰好有两列;如果用于更宽的表格,则会按行分组显示。
框体如何降级
框体不会以任意宽度绘制;它会逐步缩减,直到适配 monoWidth,如果没有任何框体能适配,则会变成列表。无法要求表格宽于气泡。
- 完整框体,逐列移除内边距(先移除右侧,再移除左侧)。
- 紧凑无边框样式,同样逐步移除内边距:
Head1|Head2 |Head-N ------+------------+------ A |BBBBBBBBBBBB|C - 自动换行框体,先尝试完整边框,再尝试紧凑样式:每列至少保留其最长单词所需的宽度,剩余宽度按比例分配,并对单元格内容进行自动换行。行会按需增高,不设上限;行与行之间始终绘制分隔线,因为两行自动换行的内容若没有分隔线,会连在一起。单元格内容在行内顶部对齐。
+---------+--------------+ | Feature | Notes here | +=========+==============+ | Alpha | short note | +---------+--------------+ | Beta | a slightly | | | longer note | +---------+--------------+ - 项目符号列表,当连最长的单词都无法容纳时(例如很长的 URL,或有 5 列且每列宽 26 个字符……)。
自动换行的高框体是否比列表更易读,可以通过预览来判断:该表格自己的面板可以将其切换为列表。无法适配任何框体的表格会在自己的面板中说明这一点,并提供列表布局,而不是提供无法产生任何变化的样式。
表格的其他行为
- 列宽按显示单元格计算,因此表情符号和 CJK 文本仍能对齐(
✅、日本語均计为 2 列)——但手机的显示能力有限:这些字形也来自备用字体,因此对齐只能尽力而为,不同于 ASCII 边框。 - 框体和紧凑样式会遵循列对齐方式(
:---、:---:、---:)。 - 只有表头的表格不会渲染空表体或重复边框。
- 单元格内的
<br>会变为空格,转义后的\|会变成¦,以免被误认为多出一列。 - 边框使用纯 ASCII 字符(
+-|=),这是有意为之。WhatsApp 的等宽字体不包含制表符号:手机会从任意备用字体中获取─和┌,其宽度也由该字体决定;由 26 个此类字符组成的分隔线会折成两行,而旁边的文本行却不会。+-|是等宽字体唯一承诺宽度一致的字符——这也是转义后的竖线会变成¦的原因,它是与à使用同一字体的 Latin-1 字符。 - 行分隔线(默认关闭)会在表体各行之间绘制分隔线,适用于所有有边框和紧凑样式;自动换行表格则始终绘制分隔线。
- 样式、行分隔线和列表布局都可以按表格单独设置:在预览中将鼠标悬停在表格上,会显示该表格自己的控件。这些控件初始值取自文档默认设置,并只覆盖该表格的设置;控件只显示仍然适用的选项(框体显示分隔线选项,列表显示布局选项)。宽度不在这些选项之中——气泡只有一个,所有表格使用相同宽度。带有独立设置的表格会保留虚线标记,因为面板顶部的控件会有意跳过它;表格的“重置”按钮会将设置交还给这些顶部控件。覆盖设置会根据表头文本跟随表格,因此在其上方添加或删除表格不会改变设置对应的表格。嵌套在列表项或引用块中的表格始终遵循文档默认设置。
代码块
围栏代码块和缩进代码块会原样传到 WhatsApp:转换器不会重新换行或调整缩进,因为代码中的换行属于内容,而非排版。WhatsApp 会自行在长行中间换行,聊天气泡也没有水平滚动功能,因此预览会在 monoWidth 处模拟换行,而不是滚动,并准确显示收件人会看到的换行位置。
其他元素
- 链接:
[text](url)→text (url);自动链接、<https://x>、[url](url)和<[email protected]>会显示为不重复的纯 URL 或地址(不会泄露mailto:) - HTML 实体: 十进制和十六进制引用,以及常见的命名实体——Latin-1 字母、标点和符号(
café→café、©→©、A→A)。较少见的引用(希腊字母、数学符号)会保留原样。 - 行内 HTML:
<b>/<strong>→*,<i>/<em>→_,<s>/<del>→~,<code>→`,<br>→ 换行;注释和其他标签会被移除 - HTML 块: 移除标签,将块边界转换为换行,并解码实体
- 引用块: 保留
>前缀,支持嵌套(> > nested) - 代码块: 使用三个反引号保留;内容中的三个反引号会替换为
ˋˋˋ,以免提前结束代码块 - 水平分隔线:
---→─────────────── - 转义字符: 使用相似的 Unicode 字符(
∗、_、∼),避免 WhatsApp 将其识别为格式标记
WhatsApp 特定处理
- 忽略单词中间的格式:
super**bold**ly→superboldly(WhatsApp 不支持单词中间的格式) - 标点符号可作为边界:
**Name**: value→*Name*: value,(**x**)和**end**.也同样适用
如何使用
- 打开网页: https://drsound.github.io/markdown-to-whatsapp/
- 将
.md文件粘贴、输入或拖放到左侧面板中。“试用示例”会填入一条示例消息。 - 右侧面板会在 WhatsApp 聊天气泡中显示消息,效果与收件人看到的完全一致;“查看原始语法”会显示将要复制的文本。两个面板会同步滚动——鼠标指针所在面板会带动另一个面板滚动;输入时,预览也会跟随光标移动。
- 选择**“复制到 WhatsApp”,或选择“在 WhatsApp 中分享”**,通过
wa.me打开一条已准备好消息的聊天。特别长的消息无法放入链接中——浏览器会截断几千个字符之后的 URL——因此“分享”选项会改为提示你复制。
界面会遵循操作系统的浅色或深色主题;标题栏中的切换按钮可覆盖此设置,选择也会被记住。选项栏按内容类型分为不同部分,只有文本包含相应内容时才会显示:气泡(存在表格或代码块时显示宽度选项)、表格(样式和分隔符)以及标题(表情符号前缀)。如果其他设置让某个控件失去作用——例如列表样式下的分隔符,或没有单色内容可绘制时的宽度——该控件会在原位变暗,而不是被移除,以保持选项栏形状不变。选项会与主题一同存储在 localStorage 中。每个表格的设置不会被存储,因为它们属于正在转换的文本。
在代码、Shell 或 Agent 中使用
同一转换器也以 markdown-to-whatsapp 的名称发布在 npm 上(需要 Node 20 或更高版本)。可用选项与上述相同,名称和默认值也一致,并会在本节末尾完整列出。
库
npm install markdown-to-whatsapp
import { convertTextToWhatsapp, convertToBlocks } from 'markdown-to-whatsapp';
convertTextToWhatsapp('# Hi **there**');
// → '*📌 Hi there*'
convertTextToWhatsapp(markdown, { monoWidth: 30, tableFormat: 'auto', headingEmojis: false });
// The same conversion with the blocks kept apart: each has the source `line` it starts on,
// and each table its `key`, `columns`, `fitsBox`, `asList` and `listLayout`
const { text, blocks } = convertToBlocks(markdown, { monoWidth: 30 });
命令行
npx markdown-to-whatsapp notes.md # a file…
cat notes.md | npx markdown-to-whatsapp # …or stdin
npx markdown-to-whatsapp notes.md --width 32 --tables list --no-emoji
npx markdown-to-whatsapp notes.md --json # the blocks, for scripting
npx markdown-to-whatsapp --help
--width(10–80)、--tables auto|list、--layout auto|rows|columns|pairs、--separator、--no-emoji、--json,以及 -h, --help 和 -v, --version。输出会写入 stdout;遇到无效选项时,会在 stderr 中说明原因并以状态码 2 退出。
MCP 服务
此程序包通过 stdio 作为 Model Context Protocol 服务运行,因此 Agent 可以自行转换文本——这对表格很重要:根据 26 个字符的气泡宽度计算列数,是模型容易出错、而此工具能正确处理的事情。
claude mcp add markdown-to-whatsapp -- npx -y markdown-to-whatsapp mcp
或者,对于 Claude Desktop 和其他接受 JSON 配置的客户端:
{
"mcpServers": {
"markdown-to-whatsapp": {
"command": "npx",
"args": ["-y", "markdown-to-whatsapp", "mcp"]
}
}
}
它提供一个工具 convert_markdown_to_whatsapp,接受 markdown,以及可选的 monoWidth、tableFormat、listLayout、rowSeparator 和 headingEmojis。转换后的文本会作为工具内容返回,结构化结果则会将其作为 text 返回,并附带 tables:每个表格对应一条记录,包含 key、columns、fitsBox、asList 和 listLayout,以便 Agent 判断哪些表格转换成了方框,哪些转换成了列表。此工具为只读且幂等。
选项
选项包括 tableFormat(auto | list)、monoWidth、rowSeparator、headingEmojis、listLayout(auto | rows | columns | pairs)和 tableOverrides——它可以是一个按表格在文档中的位置索引的数组,也可以是一个以表格的 key 为键的对象(将表头文本用 | 连接;重复的表头会附加 #2、#3……);每个条目都可以单独覆盖该表格的其他任何选项。页面仅按表格提供 listLayout 选项。
输入仍接受旧名称:tableThreshold 对应 monoWidth,ascii / always 对应 tableFormat(ascii 从不绘制宽于气泡的方框,因此会映射到 auto)。同时提供新旧名称时,以新名称为准,旧名称会被丢弃。过去用于选择 Unicode 方框线条样式的 borderStyle 仍会被接受,但会被忽略。
开发
运行测试
Node 20 或更新版本;在仓库根目录运行:
npm install
npm test
测试套件使用基于文件的测试:
tests/inputs/*.md- Markdown 输入文件tests/inputs/*.json- 可选的每个测试样例的转换器选项(例如{ "monoWidth": 40 })tests/expected/*.txt- 预期的 WhatsApp 输出
此外,还会检查一些不变量:随附的解析器与已安装的解析器一致,convertToBlocks 报告正确的源代码行,程序包入口为页面脚本。
每次推送和拉取请求都会在 CI 中运行测试(.github/workflows/test.yml)。
项目结构
docs/converter.js- 转换器本身:一个纯 ES 模块,不依赖 DOM,选项通过参数传入。它既是页面导入的脚本,也是 npm 程序包的入口(exports["."]),因此只需保留一份,无需构建步骤。它提供convertTextToWhatsapp(markdown, options)、convertToBlocks(markdown, options)——执行相同的转换,但将顶层区块分开,并为每个区块标记其所属的表格,这是按表格设置选项的基础——以及 UI 用于仅在适用时显示选项的mdContainsTable/mdContainsHeading/mdContainsCode查询。convertToBlocks的每个区块都会报告其起始源代码line——这能让两个面板保持同步滚动——每个表格区块还会报告其key、columns、fitsBox(可以绘制方框)、asList(实际输出的内容)和listLayout,以便界面准确提供剩余可选项。docs/ui.js- 页面逻辑:主题、上下文选项、WhatsApp 预览、面板间滚动同步、复制和分享docs/index.html、docs/style.css- 标记和手写样式表(不使用 CSS 框架)docs/vendor/- marked 的 ES 构建版本,由npm run vendor从node_modules复制而来;页面的导入映射会将marked解析到此处bin/markdown-to-whatsapp.js- 命令行工具;bin/mcp.js- 在mcp上启动的 MCP 服务,仅在此时加载,因此转换文件时不会加载协议 SDKscripts/vendor.js- 将 marked 的 ES 构建版本复制到docs/vendor/(npm run vendor)tests/- 测试样例和运行器
marked 依赖
marked 的版本在 package.json 中固定为 18.0.10,测试套件会核对页面从 docs/vendor/ 加载的副本是否与之匹配,因此页面、包和测试始终以相同的方式解析 Markdown。要升级版本:更改固定版本,运行 npm install、npm run vendor 和 npm test。
发布
npm test
npm pack --dry-run # docs/converter.js, bin/, README, LICENSE — nothing else
npm publish
本地开发
cd docs
python3 -m http.server 8080
# Open http://localhost:8080
许可证
MIT,参见 LICENSE 文件。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。
