最近Vibe Coding了一个在线编辑器叫 Stellar Writing。它是一个跑在浏览器里的在线博客编辑器,专门为 Hexo Theme Stellar 做了适配。本文讲清楚我为什么要做它、它解决了什么问题,以及怎么用。
为什么要再造这个轮子
Hexo 作为静态博客,本地写作的体验一直有点别扭。通用的 Markdown 编辑器(Typora、Obsidian、VS Code 插件等)对”纯 Markdown”支持得很好,但它们不认识 Stellar 的自定义标签。
Stellar 主题有 45 个块级与行内标签插件: 这些标签是 Stellar 排版能力的核心——卡片、按钮组、标签页、头图 banner、友链卡片都是靠它们实现的。问题就出在这:
- 本地 Markdown 预览器里,这些标签只是一堆
{% %}源码,原样裸露; - 主题特有的样式(卡片阴影、banner 头图、按钮配色、自定义 Front Matter 控制的文章头)在写作阶段完全不可见;
- 想看到真实效果,只能
hexo s起本地服务,再切到浏览器刷新——那不是一个专注写作的编辑器,刷新一次要等整站重新生成。
于是写作流程变成了:编辑器里写 → 起服务 → 浏览器看 → 回来改 → 再刷新。卡片好不好看、标签页排版协不协调、banner 头图位置对不对,全靠猜。我想要的就是一个左边写 Markdown、右边立刻看到文章在博客里最终样子的编辑器,而且这个”右边”必须和 Hexo 生成出来的页面一模一样。
Stellar Writing 是什么

一句话:左边写 Markdown 和 Stellar 标签,右边立刻看到文章在你博客里的样子。 它不是又一个通用 Markdown 编辑器,而是把 Stellar 主题的渲染管线整个搬进了浏览器。
预览不是”差不多像”,而是原版渲染——直接运行 Stellar 自己的标签插件源码,样式由主题原版 Stylus 编译而来,DOM 结构与 Hexo 生成的文章页保持一致。这意味着你在编辑器右边看到的卡片、按钮、标签页,和最终部署到线上的博客是同一个东西。
核心能力
| 能力 | 说明 |
|---|---|
| 原版渲染 | 直接运行 Stellar 的 45 个块级与行内标签函数,样式来自主题原版 Stylus 编译产物,结构与 Hexo 文章页一致。 |
| Front Matter 对齐 | 字段规则与 Stellar 完全相同 |
| 标签库 | 顶栏「插入标签」或 ⌘/ 唤起,可搜索、可预览语法,回车插入片段,Tab 在占位符之间跳转。 |
| 自动补全与着色 | 输入stellar标签开头弹出标签列表;标签名、key: 参数、<!-- tab --> 分隔注释单独着色。 |
| 错误提示 | 未知标签、Front Matter 格式错误,会在预览区和状态栏标出。 |
| 三种视图 | 写作 / 对照 / 预览。对照模式可拖动分栏、滚动同步;预览可切换深浅色。 |
| 文件操作 | 打开本地 .md、导出 .md、直接复制文章 HTML。草稿自动保存在浏览器本地。 |

快速上手
项目用 Bun + Next.js 构建,本地跑起来非常快:
1 | bun install |
然后打开 http://localhost:3000 即可。写好的草稿自动存在浏览器本地,随时可以导出为 .md 文件,丢进 Hexo 的 source/_posts/ 目录就能被 hexo g 正常构建。
和主题保持同步
主题升级后,编辑器要跟上。项目里 src/lib/stellar/vendor、src/lib/stellar/generated 与 public/stellar/stellar.css 都是由脚本从同级目录的 hexo-theme-stellar 自动生成的,不需要手工搬代码:
1 | bun run sync:stellar |
如果你的主题目录不在默认的 ../hexo-theme-stellar,用环境变量指定:
1 | STELLAR_THEME_DIR=/path/to/theme bun run sync:stellar |
预览用的站点配置写在项目里的 _config.stellar.yml,写法和你博客根目录的同名文件一样。把博客的配置复制过来再跑一次同步,预览就会按你博客的字体、配色渲染。
渲染流程是怎么复刻 Hexo 的
这是整个项目最关键的部分——顺序必须和 Hexo 一致,否则渲染结果就会和线上有差异。核心逻辑在 src/lib/stellar/render.ts,分四步:
围栏代码块先高亮:复刻 hexo-util 的
figure.highlight表格结构,把代码块替换成占位符,避免后续步骤误伤代码内容;标签替换为占位:把所有
{% 标签 %}换成占位,正文交给 marked 渲染(开启gfm、breaks,与 hexo-renderer-marked 默认行为一致);块级标签展开:先展开标签内部的嵌套标签,再调用原版标签函数生成 HTML;
回填占位:把标签生成的 HTML 填回占位位置,并给普通表格包上
.md-table-scroll容器。
这样一来,一篇文章从 Markdown 字符串到最终 HTML 的每一步处理,都和 Hexo 在服务器上做的事情一一对应。
已知限制
有些标签在单篇文章编辑器里没法完整渲染,这里诚实列出来:
需要站点运行时数据的标签:友链、GitHub 卡片、远程 Markdown、评分投票等依赖接口请求,预览中只显示占位说明;
{% toc wiki:xxx %}: 依赖站点 Wiki 数据,单篇编辑器中无法渲染;不能当公众号排版工具用:公众号是内联样式体系,和 Stellar 的 class 体系完全不同,硬改不现实。
部署方式
推送到 main/master 分支(或打 v* 标签)后,仓库里的 GitHub Actions 会自动构建镜像并发布到 GitHub Container Registry,同时提供 amd64 与 arm64:
1 | docker run -d --name stellar-writing -p 3000:3000 ghcr.io/seqcrafter/stellar-writing:latest |
镜像基于 Next.js 的 standalone 输出,运行时只有 Node 和构建产物,体积很小。也可以直接克隆仓库到 EdgeOne Pages 一键部署。
开源地址
项目已在 GitHub 开源:
如果它正好解决了你写 Stellar 博客时的同样痛点,欢迎 Star、提 Issue 或 PR。
后续会继续跟着 Stellar 主题的版本走,主题加新标签就同步加进标签库。写作这件事,本该在”所见即所得”里完成,而不是在 {% %} 的源码里靠想象。