publish-substack-article

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Publish Substack Article

发布Substack文章

Publish Markdown content to Substack post editor, converting Markdown to HTML and pasting as rich text. Saves as draft for user review before publishing.
将Markdown内容发布到Substack文章编辑器,将Markdown转换为HTML并粘贴为富文本。保存为草稿供用户审核后再发布。

Prerequisites

前提条件

  • Browser automation MCP (either one):
    • Chrome DevTools MCP (
      mcp__chrome-devtools__*
      )
    • Playwright MCP (
      mcp__playwright__*
      )
  • User logged into Substack
  • Python 3 with
    markdown
    package (
    pip install markdown
    )
  • copy_to_clipboard.py
    script (shared from publish-zsxq-article skill)
  • 浏览器自动化MCP(任选其一):
    • Chrome DevTools MCP (
      mcp__chrome-devtools__*
      )
    • Playwright MCP (
      mcp__playwright__*
      )
  • 用户已登录Substack
  • 安装了
    markdown
    包的Python 3环境(执行
    pip install markdown
    安装)
  • copy_to_clipboard.py
    脚本(来自publish-zsxq-article技能)

Browser MCP Tool Mapping

浏览器MCP工具映射

This skill works with both Chrome DevTools MCP and Playwright MCP. Use whichever is available:
ActionChrome DevTools MCPPlaywright MCP
Navigate
navigate_page
browser_navigate
Take snapshot
take_snapshot
browser_snapshot
Take screenshot
take_screenshot
browser_take_screenshot
Click element
click
browser_click
Fill text
fill
browser_type
Press key
press_key
browser_press_key
Evaluate JS
evaluate_script
browser_evaluate
Detection: Check available tools at runtime. If
mcp__chrome-devtools__navigate_page
exists, use Chrome DevTools MCP. If
mcp__playwright__browser_navigate
exists, use Playwright MCP.
本技能兼容Chrome DevTools MCP和Playwright MCP,使用可用的任意一种即可:
操作Chrome DevTools MCPPlaywright MCP
导航页面
navigate_page
browser_navigate
生成快照
take_snapshot
browser_snapshot
截取屏幕截图
take_screenshot
browser_take_screenshot
点击元素
click
browser_click
填充文本
fill
browser_type
按键操作
press_key
browser_press_key
执行JS
evaluate_script
browser_evaluate
检测方式:运行时检查可用工具。若存在
mcp__chrome-devtools__navigate_page
,则使用Chrome DevTools MCP;若存在
mcp__playwright__browser_navigate
,则使用Playwright MCP。

Key URLs

关键URL

  • Substack dashboard:
    https://{publication}.substack.com/publish
  • Post editor:
    https://{publication}.substack.com/publish/post/{postId}
  • Default publication:
    verysmallwoods
  • Substack控制台:
    https://{publication}.substack.com/publish
  • 文章编辑器:
    https://{publication}.substack.com/publish/post/{postId}
  • 默认发布账号:
    verysmallwoods

Editor Interface

编辑器界面

The Substack post editor uses Tiptap (ProseMirror-based WYSIWYG editor).
Substack文章编辑器使用Tiptap(基于ProseMirror的WYSIWYG编辑器)。

Key Elements

关键元素

  • Title input:
    textbox "title"
    (placeholder: "Title")
  • Subtitle input:
    textbox "Add a subtitle…"
  • Content area:
    .ProseMirror
    (Tiptap editor, "Start writing...")
  • Save status:
    button "Saved"
    (auto-saves)
  • Preview button:
    button "Preview"
  • Continue button:
    button "Continue"
    (publish flow - DO NOT USE)
  • Settings sidebar:
    button "Settings"
    (title, description, thumbnail)
  • 标题输入框:
    textbox "title"
    (占位符:"Title")
  • 副标题输入框:
    textbox "Add a subtitle…"
  • 内容区域:
    .ProseMirror
    (Tiptap编辑器,提示文字:"Start writing...")
  • 保存状态:
    button "Saved"
    (自动保存)
  • 预览按钮:
    button "Preview"
  • 继续按钮:
    button "Continue"
    (发布流程按钮 - 禁止使用)
  • 设置侧边栏:
    button "Settings"
    (可设置标题、描述、缩略图)

Settings Sidebar (left panel)

设置侧边栏(左侧面板)

When "Settings" or "File Settings" is open:
  • Title:
    textbox "Add a title..."
  • Description:
    textbox "Add a description..."
  • Thumbnail: Upload button (3:2 aspect ratio)
当“Settings”或“File Settings”打开时:
  • 标题:
    textbox "Add a title..."
  • 描述:
    textbox "Add a description..."
  • 缩略图:上传按钮(比例3:2)

Toolbar

工具栏

Bold, Italic, Strikethrough, Code, Link, Image, Audio, Video, Quote, Lists (bullet/ordered), Button, More (Code block, Divider, Footnote, LaTeX, etc.)
加粗、斜体、删除线、行内代码、链接、图片、音频、视频、引用、列表(无序列表/有序列表)、按钮、更多选项(代码块、分隔线、脚注、LaTeX等)

Content Insertion Method

内容插入方式

CRITICAL: Use clipboard paste with HTML content, NOT direct fill or plain Markdown paste.
The Tiptap editor handles HTML paste natively and renders it as rich content. The workflow is:
  1. Convert Markdown to HTML using Python's
    markdown
    library
  2. Copy HTML to system clipboard using
    copy_to_clipboard.py html
  3. Focus the editor content area
  4. Press Cmd+V (macOS) or Ctrl+V (Windows/Linux) to paste
Why HTML paste?
  • fill
    tool → Content treated as plain text, no formatting
  • Plain Markdown paste → Tiptap does NOT parse Markdown on paste
  • HTML paste → Tiptap renders HTML as rich content (headings, code blocks, links, bold, etc.)
Known limitation: Substack's editor does NOT support HTML tables. Tables will be collapsed into plain text. See Step 0: Pre-Processing for converting tables to images.
重要提示:使用剪贴板粘贴HTML内容,禁止直接填充或粘贴纯文本Markdown
Tiptap编辑器原生支持HTML粘贴,并会将其渲染为富文本内容。工作流如下:
  1. 使用Python的
    markdown
    库将Markdown转换为HTML
  2. 使用
    copy_to_clipboard.py html
    将HTML复制到系统剪贴板
  3. 聚焦编辑器内容区域
  4. 按下Cmd+V(macOS)或Ctrl+V(Windows/Linux)进行粘贴
为何选择HTML粘贴?
  • fill
    工具 → 内容会被视为纯文本,丢失所有格式
  • 纯Markdown粘贴 → Tiptap不会解析Markdown格式
  • HTML粘贴 → Tiptap会将HTML渲染为富文本内容(包括标题、代码块、链接、加粗等)
已知限制:Substack编辑器不支持HTML表格。表格会被折叠为纯文本。请查看步骤0:预处理部分了解如何将表格转换为图片。

Main Workflow

主工作流

Step 0: Pre-Processing — Convert Tables to Images

步骤0:预处理 — 将表格转换为图片

Substack does NOT render HTML tables. They collapse into plain text. Any Markdown table must be converted to a PNG image and uploaded separately.
Workflow:
  1. Detect tables in the Markdown file (lines with
    |
    forming table structure)
  2. Create styled HTML for each table:
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: white; }
table { border-collapse: collapse; width: 100%; font-size: 15px; line-height: 1.6; }
th { background: #f8f8f8; font-weight: 600; text-align: left; padding: 10px 16px; border-bottom: 2px solid #e0e0e0; }
td { padding: 8px 16px; border-bottom: 1px solid #eee; }
tr:hover { background: #fafafa; }
code { background: #f0f0f0; padding: 2px 6px; border-radius: 3px; font-size: 13px; font-family: 'SF Mono', Menlo, monospace; }
</style>
</head>
<body>
<table>
<!-- table content here -->
</table>
</body>
</html>
  1. Render to screenshot: Open the HTML file in a browser tab, take a screenshot, close the tab:
undefined
Substack不渲染HTML表格,表格会被折叠为纯文本。所有Markdown表格都必须转换为PNG图片并单独上传。
工作流
  1. 检测表格:在Markdown文件中检测表格(包含
    |
    的行组成的表格结构)
  2. 为每个表格创建带样式的HTML
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: white; }
table { border-collapse: collapse; width: 100%; font-size: 15px; line-height: 1.6; }
th { background: #f8f8f8; font-weight: 600; text-align: left; padding: 10px 16px; border-bottom: 2px solid #e0e0e0; }
td { padding: 8px 16px; border-bottom: 1px solid #eee; }
tr:hover { background: #fafafa; }
code { background: #f0f0f0; padding: 2px 6px; border-radius: 3px; font-size: 13px; font-family: 'SF Mono', Menlo, monospace; }
</style>
</head>
<body>
<table>
<!-- table content here -->
</table>
</body>
</html>
  1. 渲染为截图:在浏览器标签页中打开HTML文件,截取屏幕截图,关闭标签页:
undefined

Open HTML in new tab

在新标签页中打开HTML

browser_navigate or new_page: file:///tmp/table1.html
browser_navigate or new_page: file:///tmp/table1.html

Take screenshot

截取屏幕截图

browser_take_screenshot: filename=/tmp/table1.png, fullPage=true
browser_take_screenshot: filename=/tmp/table1.png, fullPage=true

Close tab and return to editor

关闭标签页并返回编辑器

browser_tabs: action=close

4. **Note the position** of each table in the article for later insertion (after which heading/paragraph)

5. **Remove table Markdown** from the content before HTML conversion (so it won't appear as plain text in the pasted content)

**Image upload** happens after pasting the main content — see Step 7.
browser_tabs: action=close

4. **记录每个表格在文章中的位置**:以便后续插入(在哪个标题/段落之后)

5. **在HTML转换前移除表格的Markdown内容**:避免粘贴时出现纯文本表格

**图片上传**:在粘贴主内容后进行 — 请查看步骤7。

Step 1: Prepare Content

步骤1:准备内容

Read the Markdown file and extract:
  • Title: from YAML frontmatter
    title
    field, or H1 header
    # Title
    , or filename
  • Subtitle: from YAML frontmatter
    excerpt
    or
    description
    field
  • Content: full Markdown body (strip YAML frontmatter and any cross-reference links)
读取Markdown文件并提取:
  • 标题:来自YAML前置元数据的
    title
    字段,或H1标题
    # Title
    ,或文件名
  • 副标题:来自YAML前置元数据的
    excerpt
    description
    字段
  • 内容:完整的Markdown正文(移除YAML前置元数据和任何交叉引用链接)

Step 2: Convert Markdown to HTML

步骤2:将Markdown转换为HTML

Use Python's
markdown
library with
tables
and
fenced_code
extensions:
python
import markdown
import re

with open('/path/to/article.md', 'r') as f:
    content = f.read()
使用Python的
markdown
库,并启用
tables
fenced_code
扩展:
python
import markdown
import re

with open('/path/to/article.md', 'r') as f:
    content = f.read()

Strip YAML frontmatter

移除YAML前置元数据

content = re.sub(r'^---\n.*?\n---\n', '', content, flags=re.DOTALL)
content = re.sub(r'^---\n.*?\n---\n', '', content, flags=re.DOTALL)

Strip cross-reference links (e.g., English version link)

移除交叉引用链接(例如,英文版本链接)

Adjust pattern as needed for your articles

根据你的文章调整匹配模式

content = re.sub(r'^> .* available at.*\n\n?', '', content, flags=re.MULTILINE)
content = re.sub(r'^> .* available at.*\n\n?', '', content, flags=re.MULTILINE)

Convert to HTML

转换为HTML

html = markdown.markdown(content, extensions=['tables', 'fenced_code'])
html = markdown.markdown(content, extensions=['tables', 'fenced_code'])

Write to temp file

写入临时文件

with open('/tmp/substack_article.html', 'w') as f: f.write(html)

**IMPORTANT**: Do NOT use `nl2br` extension - it converts single newlines to `<br>` tags, causing extra line breaks in the editor.
with open('/tmp/substack_article.html', 'w') as f: f.write(html)

**重要提示**:不要使用`nl2br`扩展 — 它会将单个换行符转换为`<br>`标签,导致编辑器中出现多余的换行。

Step 3: Navigate to Substack

步骤3:导航到Substack

Navigate to the Substack dashboard and create a new post:
undefined
导航到Substack控制台并创建新文章:
undefined

Navigate to Substack dashboard

导航到Substack控制台


If not logged in, prompt user to log in:
请先登录 Substack,登录完成后告诉我。 Please log in to Substack first, then let me know.
undefined

若未登录,提示用户登录:
请先登录Substack,登录完成后告诉我。 Please log in to Substack first, then let me know.
undefined

Step 4: Create New Post

步骤4:创建新文章

From the dashboard, create a new text post:
  1. Click "Create new" in the sidebar
  2. Select "Text post" (or navigate directly to a new post URL)
Alternatively, if the editor is already open with an empty post, proceed directly.
从控制台创建新的文字文章:
  1. 点击侧边栏中的“Create new”
  2. 选择“Text post”(或直接导航到新文章URL)
或者,如果编辑器已打开且为空,可直接继续。

Step 5: Fill Title and Subtitle

步骤5:填写标题和副标题

  1. Click the title textbox (
    textbox "title"
    )
  2. Type the article title
  3. Click the subtitle textbox (
    textbox "Add a subtitle…"
    )
  4. Type the subtitle/excerpt
click: title textbox
fill/type: article title

click: subtitle textbox
fill/type: article subtitle
  1. 点击标题输入框(
    textbox "title"
  2. 输入文章标题
  3. 点击副标题输入框(
    textbox "Add a subtitle…"
  4. 输入副标题/摘要
click: title textbox
fill/type: article title

click: subtitle textbox
fill/type: article subtitle

Step 6: Insert HTML Content (via Clipboard Paste)

步骤6:插入HTML内容(通过剪贴板粘贴)

CRITICAL: Do NOT use
fill
tool
- it inserts plain text without formatting.
  1. Copy HTML to system clipboard:
bash
python3 /path/to/copy_to_clipboard.py html --file /tmp/substack_article.html
  1. Click the editor content area (
    .ProseMirror
    or paragraph element inside it)
  2. Press Cmd+V to paste:
press_key: Meta+v  (macOS)
press_key: Control+v  (Windows/Linux)
This triggers Tiptap's HTML paste handler, which renders the content as rich text with proper formatting.
重要提示:禁止使用
fill
工具
— 它会插入无格式的纯文本。
  1. 将HTML复制到系统剪贴板:
bash
python3 /path/to/copy_to_clipboard.py html --file /tmp/substack_article.html
  1. 点击编辑器内容区域(
    .ProseMirror
    或其中的段落元素)
  2. 按下Cmd+V进行粘贴:
press_key: Meta+v  (macOS)
press_key: Control+v  (Windows/Linux)
这会触发Tiptap的HTML粘贴处理程序,将内容渲染为带格式的富文本。

Step 7: Insert Table Images

步骤7:插入表格图片

If the article had tables converted to images in Step 0, insert them now:
  1. Navigate to the correct position in the editor — click on the paragraph or empty line where the table should appear (after the relevant heading/text)
  2. Click the Image toolbar button (
    button "Image"
    ) — a dropdown menu appears with options: Image, Gallery, Stock photos, Generate image
  3. Click "Image" menuitem from the dropdown — a file chooser dialog opens
  4. Upload the image via file chooser:
    • Playwright MCP:
      browser_file_upload
      with the image path
    • Chrome DevTools MCP:
      upload_file
      with the image path
Important notes:
  • File path restriction: Playwright MCP only allows file uploads from within allowed roots (project directories). If your image is in
    /tmp/
    , copy it to the project directory first
  • Repeat for each table: Position cursor at the correct location, then upload each table image
  • Delete residual text: If table content was pasted as plain text (because it wasn't removed in pre-processing), select it (triple-click to select paragraph) and delete before inserting the image
如果文章在步骤0中转换了表格为图片,现在进行插入:
  1. 导航到正确位置:在编辑器中点击表格应出现的段落或空行(相关标题/文本之后)
  2. 点击工具栏中的Image按钮
    button "Image"
    )— 会出现下拉菜单,包含选项:Image, Gallery, Stock photos, Generate image
  3. 点击下拉菜单中的“Image”选项 — 会打开文件选择对话框
  4. 上传图片
    • Playwright MCP:使用
      browser_file_upload
      并指定图片路径
    • Chrome DevTools MCP:使用
      upload_file
      并指定图片路径
注意事项
  • 文件路径限制:Playwright MCP仅允许从允许的根目录(项目目录)上传文件。若图片在
    /tmp/
    目录下,请先将其复制到项目目录
  • 重复操作每个表格:将光标定位到正确位置,然后上传每个表格图片
  • 删除残留文本:如果表格内容因未在预处理中移除而被粘贴为纯文本,请选中它(三击选中段落)并删除,然后插入图片

Step 8: Verify Draft

步骤8:验证草稿

After pasting:
  1. Check the "Saved" status indicator (green dot + "Saved" text)
  2. Take a snapshot to verify content structure
  3. Optionally take a screenshot for visual verification
The editor auto-saves, so no explicit save action is needed.
粘贴完成后:
  1. 检查“Saved”状态指示器(绿色圆点 + “Saved”文字)
  2. 生成快照以验证内容结构
  3. 可选:截取屏幕截图进行视觉验证
编辑器会自动保存,无需手动执行保存操作。

Step 9: Report Completion

步骤9:报告完成

草稿已保存到 Substack。请在 Substack 中预览并手动发布。
Draft saved to Substack. Please preview and publish manually.

Post URL: https://verysmallwoods.substack.com/publish/post/{postId}
草稿已保存到Substack。请在Substack中预览并手动发布。
Draft saved to Substack. Please preview and publish manually.

Post URL: https://verysmallwoods.substack.com/publish/post/{postId}

Complete Example Flow

完整示例流程

User: "把 /path/to/my-article.md 发布到 Substack"
0. Pre-process tables (if any)
   - Detect Markdown tables
   - Create styled HTML for each table
   - Render to screenshots (open in browser, screenshot, close tab)
   - Remove table Markdown from content
   - Note insertion positions

1. Read /path/to/my-article.md
   - Extract title from frontmatter or H1
   - Extract subtitle from frontmatter excerpt
   - Get full Markdown content (with tables removed)

2. Convert Markdown to HTML
   - Strip frontmatter
   - Use markdown.markdown() with ['tables', 'fenced_code']
   - Write to /tmp/substack_article.html

3. Navigate to Substack dashboard or new post

4. Check if logged in
   - If not, prompt user to login

5. Fill title and subtitle

6. Copy HTML to clipboard + Paste
   - python3 copy_to_clipboard.py html --file /tmp/substack_article.html
   - Click editor content area
   - Press Cmd+V

7. Insert table images at correct positions
   - For each table: click position → Image button → Image menuitem → file upload

8. Verify draft saved
   - Check "Saved" status

9. Report success
   - "草稿已保存,请手动预览并发布"
用户:"把 /path/to/my-article.md 发布到 Substack"
0. 预处理表格(如果有)
   - 检测Markdown表格
   - 为每个表格创建带样式的HTML
   - 渲染为截图(在浏览器中打开、截图、关闭标签页)
   - 从内容中移除表格的Markdown
   - 记录插入位置

1. 读取 /path/to/my-article.md
   - 从前置元数据或H1提取标题
   - 从前置元数据的excerpt字段提取副标题
   - 获取完整的Markdown内容(已移除表格)

2. 将Markdown转换为HTML
   - 移除前置元数据
   - 使用markdown.markdown()并启用['tables', 'fenced_code']扩展
   - 写入 /tmp/substack_article.html

3. 导航到Substack控制台或新文章页面

4. 检查登录状态
   - 若未登录,提示用户登录

5. 填写标题和副标题

6. 复制HTML到剪贴板并粘贴
   - python3 copy_to_clipboard.py html --file /tmp/substack_article.html
   - 点击编辑器内容区域
   - 按下Cmd+V

7. 在正确位置插入表格图片
   - 每个表格:定位光标 → 点击Image按钮 → 选择Image选项 → 上传文件

8. 验证草稿已保存
   - 检查“Saved”状态

9. 报告成功
   - "草稿已保存,请手动预览并发布"

Critical Rules

关键规则

  1. NEVER click "Continue" - This starts the publish flow. Only save as draft (auto-save handles this)
  2. Always convert to HTML first - Plain Markdown will not be parsed by the Tiptap editor
  3. Use clipboard paste - The only reliable way to insert formatted content
  4. Check login status - Prompt user to login if needed
  5. Preserve original file - Never modify the source Markdown file
  6. Report completion - Tell user the draft is saved and needs manual review
  7. No
    nl2br
    extension
    - Causes double line breaks
  8. Tables → images - Pre-process tables before pasting content; upload images after paste
  9. Playwright file paths - Playwright MCP restricts file uploads to allowed roots; copy temp files to project directory before uploading
  1. 禁止点击“Continue”按钮 — 这会启动发布流程。仅保存为草稿(自动保存会处理)
  2. 必须先转换为HTML — 纯Markdown不会被Tiptap编辑器解析
  3. 使用剪贴板粘贴 — 这是插入格式化内容的唯一可靠方式
  4. 检查登录状态 — 必要时提示用户登录
  5. 保留原始文件 — 绝不修改源Markdown文件
  6. 报告完成状态 — 告知用户草稿已保存,需要手动审核
  7. 禁止使用
    nl2br
    扩展
    — 会导致双倍换行
  8. 表格转图片 — 在粘贴内容前预处理表格;粘贴后上传图片
  9. Playwright文件路径 — Playwright MCP限制文件上传到允许的根目录;上传前将临时文件复制到项目目录

Troubleshooting

故障排除

Content Shows as Plain Text (No Formatting)

内容显示为纯文本(无格式)

If you see raw HTML tags or unformatted text:
  • Cause: Content was inserted using
    fill
    tool instead of clipboard paste
  • Solution: Use the
    copy_to_clipboard.py
    + Cmd+V method (see Step 6)
如果看到原始HTML标签或无格式文本:
  • 原因:使用了
    fill
    工具而非剪贴板粘贴
  • 解决方案:使用
    copy_to_clipboard.py
    + Cmd+V的方式(参见步骤6)

Tables Not Rendering (Shows Plain Text)

表格未渲染(显示纯文本)

Substack's Tiptap editor does not support HTML tables. They collapse into inline plain text.
  • Solution: Convert tables to styled HTML → render as screenshots → upload as images (see Step 0 and Step 7)
  • Alternative: Restructure simple tables as formatted lists
  • If plain text already pasted: Triple-click the plain text paragraph to select it, press Backspace to delete, then insert the table image at that position
Substack的Tiptap编辑器不支持HTML表格,表格会被折叠为行内纯文本。
  • 解决方案:将表格转换为带样式的HTML → 渲染为截图 → 作为图片上传(参见步骤0和步骤7)
  • 替代方案:将简单表格重构为格式化列表
  • 如果已粘贴纯文本:三击选中纯文本段落,按Backspace删除,然后在该位置插入表格图片

Login Required

需要登录

If page shows login prompt:
请先登录 Substack: https://verysmallwoods.substack.com
登录完成后告诉我。
如果页面显示登录提示:
请先登录Substack: https://verysmallwoods.substack.com
登录完成后告诉我。

Editor Not Loading

编辑器未加载

If editor elements are not visible:
  1. Wait for page to fully load
  2. Take a new snapshot
  3. If still not loading, refresh the page
如果编辑器元素不可见:
  1. 等待页面完全加载
  2. 生成新快照
  3. 若仍未加载,刷新页面

Clipboard Copy Fails

剪贴板复制失败

If
copy_to_clipboard.py
fails:
  • Ensure dependencies:
    pip install pyobjc-framework-Cocoa
    (macOS)
  • Check the HTML file exists and is readable
  • Try copying a smaller test string first
如果
copy_to_clipboard.py
执行失败:
  • 确保依赖已安装:
    pip install pyobjc-framework-Cocoa
    (macOS)
  • 检查HTML文件是否存在且可读
  • 尝试先复制一个较小的测试字符串

Element Reference

元素参考

ElementSelector/IdentifierDescription
Title input
textbox "title"
Post title
Subtitle input
textbox "Add a subtitle…"
Post subtitle
Content area
.ProseMirror
(Tiptap editor)
Post content
Save status
button "Saved"
Auto-save indicator
Preview button
button "Preview"
Preview post
Continue button
button "Continue"
DO NOT USE - starts publish flow
Settings button
button "Settings"
Open settings sidebar
Exit button
button "Exit"
Exit editor
Image button
button "Image"
Opens image upload dropdown
Image menuitem
menuitem "Image"
Opens file chooser for image upload
Author button
button "{PublicationName}"
Author/publication selector
元素选择器/标识符描述
标题输入框
textbox "title"
文章标题
副标题输入框
textbox "Add a subtitle…"
文章副标题
内容区域
.ProseMirror
(Tiptap editor)
文章内容
保存状态
button "Saved"
自动保存指示器
预览按钮
button "Preview"
预览文章
继续按钮
button "Continue"
禁止使用 - 启动发布流程
设置按钮
button "Settings"
打开设置侧边栏
退出按钮
button "Exit"
退出编辑器
图片按钮
button "Image"
打开图片上传下拉菜单
图片选项
menuitem "Image"
打开文件选择对话框以上传图片
作者按钮
button "{PublicationName}"
作者/发布账号选择器

Technical Details

技术细节

Editor Stack

编辑器栈

  • Tiptap: A headless, framework-agnostic rich-text editor built on ProseMirror
  • ProseMirror: The underlying rich-text editing framework
  • Paste handling: Tiptap natively parses HTML from clipboard and converts to its internal document model
  • Tiptap:一个基于ProseMirror的无头、与框架无关的富文本编辑器
  • ProseMirror:底层的富文本编辑框架
  • 粘贴处理:Tiptap原生解析剪贴板中的HTML,并将其转换为内部文档模型

Content Conversion Pipeline

内容转换流程

Markdown file
    ↓ (Python markdown library)
HTML string
    ↓ (copy_to_clipboard.py)
System clipboard (text/html + text/plain)
    ↓ (Cmd+V keyboard shortcut)
Tiptap ProseMirror editor
    ↓ (auto-save)
Substack draft
Markdown文件
    ↓ (Python markdown库)
HTML字符串
    ↓ (copy_to_clipboard.py)
系统剪贴板(text/html + text/plain)
    ↓ (Cmd+V快捷键)
Tiptap ProseMirror编辑器
    ↓ (自动保存)
Substack草稿

Supported Formatting

支持的格式

The following Markdown elements are correctly rendered after HTML conversion and paste:
Markdown ElementSubstack SupportNotes
Headings (H2-H6)YesH1 not recommended (title is separate)
Bold / ItalicYes
Inline codeYes
Code blocksYesSyntax highlighting may vary
LinksYes
BlockquotesYes
Bullet listsYes
Ordered listsYes
Horizontal rulesYes
TablesNo → ImageConvert to styled HTML, screenshot, upload as image
ImagesManualUpload via Image toolbar button → file chooser
以下Markdown元素在转换为HTML并粘贴后可正确渲染:
Markdown元素Substack支持情况说明
标题(H2-H6)不推荐使用H1(标题单独设置)
加粗 / 斜体
行内代码
代码块语法高亮可能有所不同
链接
引用
无序列表
有序列表
水平线
表格否 → 图片转换为带样式的HTML,截图后作为图片上传
图片手动上传通过工具栏的Image按钮 → 文件选择对话框上传