朗读会在每个页面的描述下方加一个 收听本页播放器。读者按下播放,页面就从标题开始逐句朗读,被读到的句子会高亮并保持在视野内。它需要手动启用:
export default defineConfig({
narration: true,
});
设为 true 时,页面使用读者设备自带的浏览器语音朗读:无需密钥,无需构建步骤,在任何托管平台上都能工作,静态的或非静态的都可以。加上一个 provider 则改用生成的语音。
它朗读哪些内容#
朗读会按顺序读出页面标题、描述、各级标题、段落、列表项和卡片文字。有些组件前面会加一句简短的语音提示,让听者知道接下来是什么内容:
| 组件 | 提示 |
|---|---|
| 提示框 | 「注意。」「提示。」「警告。」等等 |
| Steps | 「第 1 步。」「第 2 步。」 |
| Tabs | 「macOS 标签页。」每个标签页都会被朗读,不只是当前打开的那个 |
| Accordion 和 Expandable | 「可展开区块。」 |
当朗读遇到折叠起来的区块或未显示的标签页时,会把它展开,这样高亮总能落在读者看得见的文字上。
代码块、表格、图片、视频、图表、公式、类型展示、文件树和实时组件预览都会被跳过 —— 它们读出来没有意义。
只有正文足够多、值得一听的页面才会出现播放器,大约 50 词以上。很短的页面,以及大部分由代码或 API 字段构成的页面,都不会出现播放器。
收听#
页面播放时,播放器会固定在页头下方,包含播放与暂停、上一句与下一句、进度条、已读时间与页面总长度的对比,以及 0.8× 到 2× 的倍速控制。这个倍速会被记住,下一页继续沿用。
页面会自动滚动,让正在朗读的句子保持在视野内。如果读者自己滚走了,跟随就会停止而音频继续播放;滚回那个句子,或者按下 跟随朗读,就会重新跟上。打开另一个页面会停止朗读。
浏览器语音#
narration: true 使用 Web Speech API 和读者设备上的语音来朗读页面,并按页面语言挑选一个合适的声音。如果浏览器没有该语言的声音,播放器会保持隐藏,而不是用错误的口音把页面念出来。
这不需要你提供任何东西,也不产生任何费用,但效果取决于设备:macOS、iOS 和 Windows 上的系统语音都不错,而某些 Linux 浏览器自带的语音很少,甚至一个都没有。
生成的语音#
传入一个 provider,就能在构建时用神经语音模型生成音频。这个 provider 就是 assistant 使用的同一个 gateway()适配器,只是指向一个语音模型:
import { defineConfig } from "blume";
import { gateway } from "blume/ai";
export default defineConfig({
narration: {
provider: gateway({
model: "openai/tts-1-hd",
voice: "alloy",
}),
},
});
blume build 会完全按播放器的方式读取每个构建好的页面,把它拆成句子,并通过 Vercel AI Gateway 为每个句子生成一段音频。这些音频和每页一份的小清单会作为静态文件写入你的构建产物,放在 /blume-narration/ 下,因此重播不花任何代价,也没有任何东西在服务器上运行。
在生成任何东西之前,构建会先打印它需要多少段新音频、总共覆盖多少字符,让成本一目了然:
Generating narration: 214 new clip(s), 15,880 characters, with openai/tts-1-hd
| 选项 | 默认值 | 说明 |
|---|---|---|
model |
openai/tts-1-hd |
gateway 的语音模型:openai/tts-1、openai/tts-1-hd、fish-audio/s2.1-pro、spacexai/grok-tts,以及 gateway 列出的其它模型。 |
voice |
alloy |
模型使用的声音。 |
instructions |
指定声音该如何朗读,适用于接受指令的模型(「像一位老师那样,平静地朗读」)。 | |
apiKeyEnv |
AI_GATEWAY_API_KEY |
保存 gateway 密钥的环境变量。在 Vercel 上,构建的 OIDC token 同样可用。 |
headers |
随每次请求发送的静态响应头。 | |
providerOptions |
原样传给 AI SDK 的 generateSpeech,用于 Blume 没有具名列出的模型设置。 |
缓存#
音频会按决定其声音的那些条件缓存在 node_modules/.cache/blume/narration 下:句子本身、它的语言、模型、声音和指令。重新构建时只需为发生变化的句子付费,被多个页面共用的句子只生成一次。Vercel 和 Netlify 会从各自的构建缓存中恢复 node_modules,所以在那里部署只会重新生成变化的部分。在其它 CI 上,请在两次运行之间缓存这个目录。
回退#
在没有音频的地方,生成的语音会回退到浏览器语音:
- 在
blume dev中,它从不生成音频。运行blume build和blume preview就能在本地听到生成的语音。 - 构建时未设置密钥。构建会发出警告并跳过生成。
- 生成失败时。由于 AI SDK 已经重试过,构建会在第一段失败的音频处停下,并保留所有已缓存的音频。
语言#
朗读会用每个页面自身的内容语言进行,因此在一个国际化的站点上,每个 locale 都用自己对应的声音朗读。这些语音提示和播放器标签在每个内置 UI 语言中都已翻译。要按 locale 覆盖它们,在 i18n.ui 中改写 narration 下的内容。
为单个页面关闭它#
在页面的 frontmatter 中设置 narration: false,就能让该页面不出现播放器:
---
title: Changelog
narration: false
---
让内容不被朗读#
给某个元素加上 data-blume-narration="skip",就能让它及其内部的一切都不参与朗读。Blume 就是靠它跳过自己的类型展示和预览的,对你自己的组件和交互岛同样有效:
<div data-blume-narration="skip">
<PricingCalculator />
</div>
分析#
读者开始收听时,播放器会发送一个 narration_play 事件,附带 engine(audio 或 browser)和页面的 path;页面播放到结尾时再发送 narration_complete。这两个事件和其它自定义事件一样,都会经过你的分析适配器。