技术预览提交反馈

通过 escape 覆盖组件

更新于 2026年11月12日

Livemark 附带一套 React 组件库、shadcn 风格的基础元素和一份基础样式表。每个文件都可以通过在 .livemark/ 下放置同名文件来覆盖:

要覆盖的文件放置文件的位置
source/components/<Name>.tsx.livemark/components/<Name>.tsx
source/elements/<name>.tsx.livemark/elements/<name>.tsx
source/styles/<name>.css.livemark/styles/<name>.css
source/routes/<route>.tsx.livemark/routes/<route>.tsx
source/client.tsx, server.ts, router.tsx, head.ts.livemark/<同名文件>

Markdown 指令渲染以其命名的组件::::card 对应 components/Card.tsx,::video 对应 components/Video.tsx,一组卡片对应 components/Cards.tsx。note、tip、info、warning 和 danger 提示框都渲染 components/Callout.tsx。

source/ 路径位于 node_modules/livemark/ 中;渲染 Markdown 的组件、元素和样式位于 node_modules/@livemark/markdown/ 中,livemark escape --list 会同时列出两者。.livemark/public/ 下的文件会加入站点的静态资源,并从站点根路径提供。

无需修改任何 import。Livemark 的其余部分会自动使用被覆盖的文件。

livemark escape 命令

查找并复制要覆盖的默认文件这一步已经自动化:

# Pick files from a searchable list:
livemark escape

# Print every overridable file:
livemark escape --list

# Copy specific defaults into .livemark/:
livemark escape components/Footer.tsx elements/button.tsx

在终端中,livemark escape 会打开一个选择器:输入文字进行筛选,按 Tab 选择,按 Enter 复制。已经覆盖的文件会显示出来,但无法选中。在终端之外(管道、CI)运行时,它会像 --list 一样打印列表。

livemark escape 不会覆盖已存在的文件。如果想从头开始,请先删除你的覆盖文件。

删除覆盖文件即可恢复默认文件:开发服务器运行时会立即生效,否则在下一次 start 或 build 时生效。

livemark escape components/Footer.tsx

然后编辑 .livemark/components/Footer.tsx:

.livemark/components/Footer.tsx
export function Footer() {
  return (
    <p className="mt-12 text-center text-sm text-muted-foreground">
      Built by my team · 2026
    </p>
  )
}

保存后,开发服务器会立即应用它。

示例:重新设计按钮样式

livemark escape elements/button.tsx

编辑 .livemark/elements/button.tsx 中 cva 的类名列表,即可全局修改按钮样式。由于 Livemark 自带的每个组件都通过覆盖机制导入 Button,你的变体会在所有地方生效。

示例:调整设计令牌

livemark escape styles/general.css

编辑 .livemark/styles/general.css 中的 :root 块来更换品牌风格:

:root {
  --primary: oklch(0.55 0.24 27); /* red */
}

在覆盖文件中导入其他 Livemark 模块

在覆盖文件中,可以通过 livemark/<subdir>/* 子路径导出导入其他 Livemark 模块:

.livemark/components/Footer.tsx
import { cn } from "cn"
import { Button } from "livemark/elements/button"

这些路径在构建时解析,并经过同样的覆盖查找:如果你也覆盖了 button,那么这里同样会使用你的版本。

覆盖文件的类型检查

escape 出来的文件会保留其所复制的默认文件中的相对 import(../hooks/site.ts)。这些 import 在 Livemark 为 start 和 build 生成的挂载目录中解析,而不是在 .livemark/ 旁边,因此你项目自身的 tsc --noEmit 无法检查 escape 出来的文件,请把 .livemark/ 排除在其程序之外。真正校验覆盖文件的是 livemark build:它会把覆盖文件与 Livemark 的其余部分一起打包,并在 import 无法解析时失败。

你自己编写、且只从 livemark/<subdir>/* 导出路径 import 的文件可以进行类型检查,包括提供站点配置的 livemark:virtual 模块。它的类型随 livemark 包一起加载:任何包含 livemark.config.ts 的程序都已经具备这些类型。不包含它的程序只需在一个 .d.ts 文件中加一行:

.livemark/env.d.ts
/// <reference types="livemark/env" />

不要自己声明 livemark:virtual:手写的声明会与真实的配置类型逐渐脱节。

无法以这种方式覆盖的内容

  • 新页面:它们属于自定义路由,添加在同一个 .livemark/routes/ 文件夹下。
  • 服务端代码(actions/、commands/):无法覆盖;如需修改,请 fork 仓库。
  • 配置 schema:livemark.config.ts 的结构是固定的。

另请参阅

  • 语法主题:如果只想更改代码的配色,这是更轻量的选项。
  • 自定义路由:编写新页面(而不是替换现有页面)。

用 ❤ 和 Livemark 构建