关于 Phonebook
Phonebook 是可自托管的命令行工具和 MCP 服务,可将现有 SwiftUI `#Preview` 与 Jetpack Compose `@Preview` 转为静态图库。它面向需要与设计师共享 UI 预览的应用团队,使用各平台的预览渲染工具生成内容。
主要功能
- 01复用现有 UI 预览
- 02生成静态 HTML 图库
- 03按组件和状态分组卡片
- 04支持 Android 与 iOS 渲染流程
- 05检查配置和预览覆盖情况
适用场景
- 01向设计师展示应用预览界面
- 02查找缺少预览或暗色变体的组件
- 03仅重新生成已修改文件中的预览
MCP 相关
运行 `npx @stag-build/phonebook mcp` 可启动 Phonebook 的 MCP 服务。其工具可供编码 Agent 检查配置、分析预览覆盖情况、获取指导、生成预览并构建图库。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
5收录的工具列表 · 更新于 1 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dir | string | 否 | 包含 phonebook.config.json 的项目目录 |
| paths | array | 否 | 仅报告这些文件或目录中声明的组件,路径相对于项目目录。仍会扫描整个项目。 |
| changed | boolean | 否 | 仅报告存在未提交 git 更改的文件中的组件——即你刚刚编辑的内容。在 git 仓库之外时忽略。与 paths 同时提供时,会组合使用。 |
详细介绍
一款可自托管的开源替代方案,可替代 Emerge Tools Snapshots:从现有的 Compose @Preview 和 SwiftUI #Preview 中提取内容,生成可浏览的静态 HTML 图库——无需 SaaS 账号。
Phonebook 可将团队已有的截图转换为类似 Storybook 的组件图库。无需编写新的测试代码,也无需手动维护设计令牌——它会将代码库中已有的内容渲染为静态网站,设计师无需安装任何东西即可打开。每个代码仓库都独立运行 Phonebook;v1 仅支持单平台,因此一个 Android 代码仓库(或一个 iOS 代码仓库)会生成一个 bundle 和一个网站。
功能
- 无需编写新的测试代码 — 重用已有的
@Preview/#Preview - 无需 SaaS 账号 — 可自托管,完全在 CI 或本地运行
- MCP 优先 — 编码 Agent 可为你检查设置、分析覆盖率、添加缺少的 preview,并构建图库
- 智能组件分组 — 根据 preview 名称推断
component / state卡片,无需添加注解 - 跨平台 — Android(Roborazzi + ComposablePreviewScanner,在 JVM 上运行,无需模拟器)和 iOS(SnapshotPreviews,在模拟器上运行)
- 版本感知设置 —
init/doctor会根据项目的 Kotlin 版本解析库版本,并在 Kotlin/Roborazzi 元数据不匹配导致难以诊断的编译崩溃之前发现问题
演示

从 samples/ios 生成的图库——根据应用自身的 #Preview 分组为 component / state 卡片,无需额外注解。
工作原理
phonebook generate会运行平台的 preview 渲染引擎,并将输出提取到一个 bundle(manifest.json+images/)中。- Android:Roborazzi + ComposablePreviewScanner,通过 Robolectric 在 JVM 上运行。无需模拟器,可在 Linux CI 上运行。
- iOS:SnapshotPreviews,通过
xcodebuild test在模拟器上运行。需要 macOS。
phonebook build会将该 bundle 转换为静态网站——默认情况下,它会将index.html直接写入 bundle 目录(复用其中已有的图像,不进行复制),因此网站位于<bundle>/index.html。传入-o <dir>则会将所有内容复制到独立的网站目录(用于发布到其他位置,或之后合并多个 bundle)。纯 HTML/CSS/JS,可从file://或任何静态主机运行。
安装
npm
npm install -g @stag-build/phonebook
Homebrew
brew install stag-build/phonebook/phonebook
也可以先添加 tap,再安装:
brew tap stag-build/phonebook
brew install phonebook
Formula 源码:stag-build/homebrew-phonebook。
无需安装(npx)
npx @stag-build/phonebook <cmd>
大多数人不会直接运行 CLI — Phonebook 是通过 MCP 服务由编码 Agent(Claude Code、Codex 等)驱动的。Agent 会添加预览、运行设置检查,并为你生成图库;底层的 CLI 是它调用的引擎。
服务通过 npx @stag-build/phonebook mcp 运行 — 无需安装。请在下方选择你的客户端。
Claude Code
claude mcp add phonebook -- npx -y @stag-build/phonebook mcp
Codex CLI
添加到 ~/.codex/config.toml:
[mcp_servers.phonebook]
command = "npx"
args = ["-y", "@stag-build/phonebook", "mcp"]
Claude Desktop
添加到 Claude Desktop 配置中(macOS 上为 ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"phonebook": {
"command": "npx",
"args": ["-y", "@stag-build/phonebook", "mcp"]
}
}
}
Cursor
添加到 .cursor/mcp.json(项目级)或 ~/.cursor/mcp.json(全局):
{
"mcpServers": {
"phonebook": {
"command": "npx",
"args": ["-y", "@stag-build/phonebook", "mcp"]
}
}
}
Xcode(Codex Agent) — Xcode 26.3+
添加到项目工作区根目录下的 .codex/config.toml。Xcode 的 Agent 使用精简的 PATH 运行,因此命令会在 shell 中包装 npx,先添加常见的 Homebrew/nvm 路径:
[mcp_servers.phonebook]
command = "/bin/zsh"
args = [
"-lc",
"PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin; npx -y @stag-build/phonebook mcp"
]
enabled = true
Xcode(Claude Code Agent) — Xcode 26.3+
将 mcpServers 块添加到 ~/Library/Developer/Xcode/CodingAssistant/ClaudeAgentConfig/.claude.json:
{
"mcpServers": {
"phonebook": {
"command": "/bin/zsh",
"args": [
"-lc",
"PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin; npx -y @stag-build/phonebook mcp"
]
}
}
}
Android Studio(Gemini Agent Mode): 暂不支持 — 它的 MCP 集成只能连接远程 httpUrl 服务,不能连接 Phonebook 这类本地 stdio 进程。请改为在 Android 仓库中使用上述基于终端的客户端(Claude Code、Codex CLI)。
然后,在 Android 或 iOS 仓库的聊天窗口中,只需提出:
“使用 phonebook MCP,为我的设计师创建一个目录。”
Agent 会处理其余工作 — 检查设置、补齐缺失的预览、生成内容并构建网站。对于更有针对性的请求,它还提供:check_setup(设置诊断,与 phonebook doctor 相同)、analyze_coverage(缺少预览或深色变体的组件)、get_preview_guidance、run_generate 和 run_build。
快速入门:Android
先运行 phonebook init — 它会检测项目的 Kotlin 版本,并打印与该版本兼容的库版本说明(例如,Kotlin 2.0 项目使用 Roborazzi 1.60.0;Kotlin 2.2+ 使用最新版本)。以下版本适用于当前 Kotlin 项目(完整的可运行示例请参见 samples/android/app/build.gradle.kts):
// app/build.gradle.kts
plugins {
id("io.github.takahirom.roborazzi") // root build.gradle.kts: version "1.72.0" apply false
}
roborazzi {
generateComposePreviewRobolectricTests {
enable = true
packages = listOf("dev.stag.phonebook.sample") // your app's package
}
}
dependencies {
testImplementation("org.robolectric:robolectric:4.14.1")
testImplementation("io.github.takahirom.roborazzi:roborazzi:1.72.0")
testImplementation("io.github.takahirom.roborazzi:roborazzi-compose:1.72.0")
testImplementation("io.github.sergio-sastre.ComposablePreviewScanner:android:0.9.3")
testImplementation("io.github.takahirom.roborazzi:roborazzi-compose-preview-scanner-support:1.72.0")
testImplementation("androidx.compose.ui:ui-test-junit4") // version from your Compose BOM, or pin one
}
在 settings.gradle.kts 旁边添加 phonebook.config.json:
{
"appName": "My Android App",
"platform": "android",
"android": { "modules": [":app"], "variant": "debug" }
}
然后,在包含 Phonebook 的仓库中运行:
npx @stag-build/phonebook generate -C /path/to/your/android/repo
npx @stag-build/phonebook build -C /path/to/your/android/repo
打开 phonebook-out/index.html。
generate --changed(或 --files A.kt,B.kt)只会渲染这些文件中声明的预览,无需额外设置。仅在本次运行中,Phonebook 会向 Gradle 传递一个初始化脚本,替换预览测试器,以保留指定的预览并仅扫描其所属的包。两个文件都会写入 build/phonebook/,绝不会写入源文件目录。如果你的项目配置了自己的 testerQualifiedClassName,则会优先使用你的设置,并渲染该模块中的所有预览。
快速入门:iOS
将 SnapshotPreviews SPM 包添加到项目中,并添加一个继承自 SnapshotTest 的小型 XCTest 目标(完整的可运行示例请参见 samples/ios):
// PhonebookSnapshotTests.swift
import Foundation
import SnapshottingTests
final class PhonebookSnapshotTests: SnapshotTest {
override class func snapshotPreviews() -> [String]? {
guard let raw = ProcessInfo.processInfo.environment["SNAPSHOTS_ONLY_FILTER"], !raw.isEmpty else {
return nil // record every #Preview
}
return raw.components(separatedBy: "\n")
}
}
读取 SNAPSHOTS_ONLY_FILTER 后,phonebook generate --changed(或 --files A.swift,B.swift)就能只渲染你刚编辑的文件中的预览;如果未设置该变量,则会像以前一样记录每个 #Preview。
在 .xcodeproj 旁边添加 phonebook.config.json:
{
"appName": "My iOS App",
"platform": "ios",
"ios": {
"project": "MyApp.xcodeproj",
"scheme": "MyApp",
"simulator": "iPhone 17 Pro"
}
}
你的 scheme 必须构建并测试快照测试目标(参见示例中的 PhonebookSample.xcscheme)。然后:
npx @stag-build/phonebook generate -C /path/to/your/ios/repo
npx @stag-build/phonebook build -C /path/to/your/ios/repo
打开 phonebook-out/index.html。
命名约定
Phonebook 会根据你现有的预览名称,将截图归入 component / state 卡片,无需添加注释。完整规则和示例请参阅 docs/naming-convention.md。
配置
phonebook.config.json:
| 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
appName | string | — | 必填。显示在图库标题中。 |
platform | "android" | "ios" | — | 必填。 |
output | string | "phonebook-out" | Bundle 输出目录,相对于配置文件所在目录。 |
android.modules | string[] | [":app"] | 要录制的 Gradle 模块。 |
android.variant | string | "debug" | 构建变体;Phonebook 会运行 <module>:recordRoborazzi<Variant>。 |
ios.project | string | — | .xcodeproj 的路径,相对于配置文件所在目录。必须设置 project 或 workspace 之一。 |
ios.workspace | string | — | .xcworkspace 的路径,相对于配置文件所在目录。 |
ios.scheme | string | — | 必填。包含 SnapshotPreviews 测试目标的 Scheme。 |
ios.simulator | string | "iPhone 17 Pro" | 用于 -destination 的模拟器设备名称。 |
ios.onlyTesting | string | 自动检测 | -only-testing: 筛选条件,让 generate 只运行快照类,而不是应用的整个测试套件。根据 SnapshotTest 子类自动推导;设为 "" 可运行全部测试。 |
Android 预览的渲染方式与 Android Studio 一致:没有自定义尺寸的 @Preview 会根据内容调整尺寸,而全屏(fillMaxSize)内容则会填满 Robolectric 屏幕;Roborazzi 默认使用 Pixel 4a。在 generateComposePreviewRobolectricTests 代码块中设置 robolectricConfig 会替换此默认值,因此请包含 "qualifiers" to "RobolectricDeviceQualifiers.Pixel4a"(或使用 Android Studio 手机对应的 "\"w411dp-h891dp-port\"")。否则,全屏预览会在 320×470dp 的屏幕上渲染。phonebook doctor 会指出这一点。
generate 和 build 都接受 -C <dir>(包含 phonebook.config.json 的项目目录)。generate 接受 -o <dir>,用于覆盖 Bundle 输出目录,并接受 --allow-empty,用于允许运行未录制到任何预览的情况。build 接受可选的 Bundle 路径;若未指定,则使用项目的 Bundle 目录。它还接受 -o <dir>,用于设置站点输出目录;若未指定 -o,build 会直接将 index.html 写入 Bundle 目录,并就地复用其中的 images/(不进行复制);上文的快速入门使用的就是这种方式。传入 -o <dir> 可将 Bundle 中的图片复制到单独的独立站点目录。
phonebook init 和 phonebook doctor
phonebook init 会检测你的平台,并生成 phonebook.config.json 以及依赖项和设置代码片段;它会根据项目的 Kotlin 版本解析库版本,并填入应用包名。它绝不会替你修改构建文件。
对于 iOS,使用 xcrun simctl list devices available 检查已安装的设备,然后运行 phonebook init --simulator "<device name>" 记录你选择的模拟器。如果 phonebook.config.json 已存在,同一命令只会更新 ios.simulator,因此重试设置时也能使用。Android 不使用 --simulator。
phonebook doctor 会检查 generate 所需的所有配置是否就绪:插件和测试依赖项(使用 Gradle 版本目录时会通过版本目录解析)、扫描器的 packages 值、Kotlin/Roborazzi 兼容性,以及工具链(JDK/Xcode/模拟器)。添加 --deep 还会编译测试源代码——速度较慢,但当静态检查结果与实际情况不符时,可以提供权威判断。在 iOS 上,如果已链接 SnapshotPreviews,但尚不存在 SnapshotTest 子类,doctor 会指出应将其添加到的确切目标和文件夹(从 .pbxproj 解析得出),让你无需在不知道位置的情况下只收到“添加这个类”的提示。
phonebook init --write-snapshot-class 是 init 不修改项目文件这一规则的唯一例外:当 doctor 的 iOS 检查确认了链接目标,且该目标的源文件夹属于 Xcode 的文件系统同步组时,它会直接写入 <folder>/PhonebookSnapshots.swift——这样做是安全的,因为 Xcode 会自动收录同步文件夹,因此不会修改 project.pbxproj。其他所有情况下都会拒绝执行并说明原因:尚未配置 SnapshotPreviews 链接、项目不使用同步组,或者子类已存在。
phonebook mcp 会运行 MCP 服务——有关设置和提示词示例,请参阅上文“与编码 Agent 搭配使用”。
要求
Android:JDK 17+。无需模拟器——Roborazzi 通过 Robolectric 在 JVM 上渲染,因此 generate 可在 Linux CI 上运行。
iOS:安装了 Xcode 的 macOS,以及已启动或可启动的模拟器(generate 会针对指定的模拟器目标运行 xcodebuild test)。CI 需要使用 macOS 运行器。
CI 配置示例请参阅 docs/ci.md,命名规则请参阅 docs/naming-convention.md。
路线图
v1(M5)之后,尚未实现:
- 在生成的图库中搜索和筛选
- 合并多个 bundle 并提供并排视图(适用于跨平台网站)
- 比较两次运行之间的版本差异(清单已包含提交和图像哈希,可用于实现此功能)
- 补充 CI 配置示例文档
许可证
MIT — 请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。
