最近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。草稿自动保存在浏览器本地。
front-matter演示
front-matter演示

快速上手

项目用 Bun + Next.js 构建,本地跑起来非常快:

1
2
bun install
bun run dev

然后打开 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,分四步:

  1. 围栏代码块先高亮:复刻 hexo-util 的 figure.highlight 表格结构,把代码块替换成占位符,避免后续步骤误伤代码内容;

  2. 标签替换为占位:把所有 {% 标签 %} 换成 占位,正文交给 marked 渲染(开启 gfm、breaks,与 hexo-renderer-marked 默认行为一致);

  3. 块级标签展开:先展开标签内部的嵌套标签,再调用原版标签函数生成 HTML;

  4. 回填占位:把标签生成的 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 主题的版本走,主题加新标签就同步加进标签库。写作这件事,本该在”所见即所得”里完成,而不是在 {% %} 的源码里靠想象。

站内搜索

没有找到内容!