文章

Markdown Image Paster:把 Typora 的图片粘贴体验搬回 JetBrains IDE

Markdown Image Paster:把 Typora 的图片粘贴体验搬回 JetBrains IDE

背景

我以前写博客主要用 Typora。

好用是真的好用。尤其是图片:截图、粘贴、落盘、插入引用,一套流程基本不用操心。

后来 Typora 开始收费了。软件收费天经地义,这个没啥好吐槽的。只是我平时写代码基本都在 JetBrains IDE 里,写技术博客又经常要一边看代码、一边整理思路。文章写到一半,还要在 Typora 和 IDE 之间来回切,时间长了就有点割裂。

于是我慢慢把博客写作也搬回了 JetBrains IDE。Markdown 本身没什么问题,真正烦人的是图片。

对 Jekyll、Hexo 这类静态博客来说,图片不是随便找个地方一放就完事。文章文件在哪里,图片放到哪个目录,Markdown 里引用什么路径,最后生成出来能不能正常访问,这些都有约定。

比如我希望一篇文章大概长这样:

1
2
_posts/2026-06-11-我的文章.md
img/2026-06-11-我的文章/image-20260612211415.png

如果一篇文章只贴一张图,手动保存、复制路径、改引用,也不是不能忍。但技术文章经常要贴截图,这个动作重复几次,就会变成一种稳定的小麻烦。

所以我写了一个 IntelliJ 插件:Markdown Image Paster

它只做一件事:

在带 typora-root-urlmedia_subpath 的 Markdown 文件里粘贴图片时,根据 front matter 规则保存图片,并插入正确的 Markdown 图片引用。

它解决的不是 Markdown 语法问题

Markdown 插入图片的语法很简单:

1
![alt](path/to/image.png)

麻烦不在语法,而在路径。

一张图从剪贴板进来以后,至少要回答几个问题:

  1. 图片应该保存到哪里?
  2. 文件名应该叫什么?
  3. Markdown 里应该插入相对路径、绝对路径,还是站点根路径?
  4. 路径里有中文、空格、标点时,要不要 encode?
  5. 如果路径写错了,会不会把图片写到项目外面?

这些问题单独看都很小,但每次粘贴图片都要处理一遍,就很影响写作节奏。

这个插件的目标不是做一个通用的图片管理器,而是把这些固定动作收掉:你在 front matter 里告诉它图片规则,它在粘贴时按规则把剩下的事情做完。

插件什么时候接管粘贴

这个边界我收得比较窄。

插件只在下面几个条件同时满足时接管粘贴:

  1. 当前文件是 Markdown
  2. 剪贴板里有图片
  3. 当前 Markdown 有 front matter
  4. front matter 里有 typora-root-urlmedia_subpath

只要有一个条件不满足,插件就把粘贴动作交还给 IDE 默认逻辑。

这个设计是有意为之。普通 README、临时笔记、项目文档,不应该被博客插件影响。只有当一篇文章显式写了图片路径字段,插件才认为它有图片目录约定。

换句话说:没有可识别的路径提示,就不猜。

两种路径规则

目前插件只识别两个字段:typora-root-urlmedia_subpath

这两个字段看起来都和图片路径有关,但语义不一样。

typora-root-url

typora-root-url 按 Typora 的兼容语义处理。

它表示资源根路径,不是最终图片目录。插件会先解析这个 root,再继续追加:

1
img/<markdown文件名>/

比如 front matter 是:

1
2
3
---
typora-root-url: /assets
---

当前文件是:

1
hello-world.md

最终图片目录就是:

1
assets/img/hello-world/

这里容易混淆的是:typora-root-url 不是“图片目录”,而是“资源根目录”。所以后面还会有一层 img/<文章名>/

这个行为主要是为了兼容我之前在 Typora 里写博客时形成的目录习惯。

media_subpath

media_subpath 更直接。

它表示目标图片目录本身,插件不会再追加 img/<文章名>/

比如:

1
2
3
---
media_subpath: /img/2026-06-11-hello-world/
---

最终图片目录就是:

1
img/2026-06-11-hello-world/

所以可以简单理解成:

  • typora-root-url:资源根路径,插件帮你补 img/<文章名>/
  • media_subpath:图片目录本身,写什么就用什么

如果 front matter 里没有这两个字段,插件不会走默认目录规则,而是直接回到 IDE 默认粘贴行为。路径这种事情,宁可明确一点。

绝对路径和相对路径

front matter 里的路径还要解决一个问题:从哪里开始算?

插件采用的规则和 Typora 的使用习惯保持接近:

  • / 开头:从当前 project 根目录开始解析
  • 不以 / 开头:从当前 Markdown 文件所在目录开始解析
  • 规范化以后,路径不能逃逸出当前 project

比如当前文件在:

1
_posts/hello-world.md

如果写:

1
media_subpath: images

最终目录就是:

1
_posts/images/

如果写:

1
media_subpath: /img/hello-world

最终目录就是:

1
img/hello-world/

至于 ../../outside 这种路径,规范化后会逃逸出 project,插件不会写文件。

这里做了两层防护:路径解析时检查一次,真正写文件前再检查一次。文件系统操作不怕多一道校验,怕的是误写。

图片文件名

图片文件名一开始我想过直接使用 Markdown 文件名,比如:

1
2026-06-04-Claude Code 权限模式:四种模式怎么选-20260612211415.png

实际用下来不太对。

图片名应该尽量描述图片本身,而不是文章本身。一篇文章里贴十张图,如果文件名都长得差不多,后面再回头看目录,会很难分辨。

现在的规则是:

  1. 剪贴板能提供原图片文件名,就用原文件名
  2. 剪贴板拿不到原文件名,就使用 image
  3. 最后统一追加 yyyyMMddHHmmss 时间戳

例如:

1
2
cover-20260612211415.png
image-20260612211415.png

如果是直接截屏,剪贴板通常拿不到原文件名,所以多数情况下会得到 image-时间戳。这个结果反而比较自然:截图本来就没有稳定的源文件名。

Markdown 引用路径

磁盘路径和 Markdown 引用路径不是一回事。

磁盘上可以保存中文、空格、中文标点:

1
img/2026-06-04-Claude Code 权限模式:四种模式怎么选/image-20260612211415.png

但 Markdown 里的图片路径更接近 URL。如果原样写:

1
![image](/img/2026-06-04-Claude Code 权限模式:四种模式怎么选/image-20260612211415.png)

有些预览器能认,有些静态站点生成器也能处理。但发布到浏览器环境以后,空格、中文标点、特殊字符都有可能带来兼容性问题。

所以插件做了一个折中:

  1. 磁盘真实路径不 encode
  2. Markdown 引用使用从 project 根开始的 / 路径
  3. Markdown 引用按 path segment 做 URL encode
  4. / 本身不 encode

比如真实路径是:

1
img/文章 标题/image name.png

插入 Markdown 时会变成:

1
![image](/img/%E6%96%87%E7%AB%A0%20%E6%A0%87%E9%A2%98/image%20name.png)

这里不能把 / encode 成 %2F。否则路径层级就坏了。

实现取舍

这个插件的实现不复杂,真正需要想清楚的是边界。

第一,不能影响正常粘贴。

粘贴是编辑器里非常基础的动作。如果插件判断不了当前场景,就应该交还给 IDE 原来的粘贴逻辑。比如不是 Markdown、剪贴板里没有图片、front matter 里没有可识别的路径字段,这些情况都不应该强行处理。

第二,路径规则要稳定。

typora-root-urlmedia_subpath 虽然都写在 front matter 里,但含义不一样。前者是资源根路径,后者是图片目录本身。如果把这两个字段混成一种逻辑,短期看省事,长期一定会让路径变得不可预测。

第三,写文件前必须守住 project 边界。

front matter 是用户手写的,路径里可能出现 ../。插件可以支持相对路径,但不能允许它一路跳出当前 project。否则一次粘贴图片,就可能把文件写到意料之外的位置。

所以整体实现其实就是把这三件事串起来:先判断要不要接管,再根据 front matter 算目标目录,最后写入图片并插入 Markdown 引用。

使用方式

插件已经发布到 JetBrains Marketplace,源码也放在了 GitHub

安装完成后,在文章 front matter 里加上路径字段:

1
2
3
4
---
title: Hello World
typora-root-url: ../
---

或者:

1
2
3
4
---
title: Hello World
media_subpath: /img/hello-world/
---

然后复制一张图片,在 IDE 里按 Cmd+VCtrl+V

插件会保存图片,并插入类似下面的引用:

1
![image-20260612211415](/img/hello-world/image-20260612211415.png)

如果路径里有中文或空格,Markdown 引用会自动 encode。

适合谁用

这个插件适合下面这类场景:

  1. 使用 JetBrains IDE 写 Markdown
  2. 使用 Jekyll、Hexo 或类似静态博客
  3. 图片目录和文章路径有固定约定
  4. front matter 里已经维护了 typora-root-urlmedia_subpath
  5. 不想每次手动保存图片、复制路径、改 Markdown 引用

如果只是临时写 README,或者不想在文章里维护 front matter,那这个插件不会接管粘贴行为。

功能越窄,误伤越少。这个插件就是按这个思路做的。

总结

Markdown Image Paster 本质上是一个很小的提效插件。它没有复杂算法,也没有宏大的架构设计,最后还是回到几个朴素动作:读 front matter、算路径、写文件、插入文本。

它的核心规则可以总结成几条:

  1. 只处理带 typora-root-urlmedia_subpath 的 Markdown 文件
  2. typora-root-url 兼容 Typora 资源根语义,media_subpath 表示目标图片目录
  3. front matter 路径可以相对当前文件,也可以从 project 根开始,但不能逃逸出 project
  4. 磁盘路径保持原样,Markdown 引用从 project 根开始,并按 path segment 做 URL encode
  5. 图片名优先使用剪贴板原文件名,拿不到时用 image-时间戳

对我这种经常写技术博客的人来说,少一次手动保存图片,少一次路径修改,少一次发布后 404,ROI 就已经够了。

参考

本文由作者按照 CC BY 4.0 进行授权