SwiftUI 组件预览
从一个小的、确定性的 SwiftUI 固定状态(fixture)创建组件渲染图。需要 macOS 14 或更高版本以及 Xcode 15 或更高版本(Swift 5.9+);不需要 Swift 6。项目引导、HTML 画廊和实时构建器还需要 Python 3.8 或更高版本(仅限标准库)。选择 PNG 进行静态检查,选择画廊进行图像比较,或选择原生窗口进行真实的固定状态交互。这些辅助工具不提供 Xcode Canvas 或热重载。
选择输出
在构建任何东西之前,按以下顺序决定输出:
-
明确的词语。 如果请求或技能参数以
png、gallery或live(对于 PNG 也可以是 "screenshot"、"render";对于 live 也可以是 "window"、"interactive")开头或命名,则使用该输出。 -
请求的意图。
用户想要 输出 查看外观,检查布局、间距、文本、状态、语言环境 PNG 比较浅色和深色,或带边框和不带边框,并排显示或通过切换 Gallery 点击、输入、切换、导航、打开表单或警告、检查流程 Live window 当一个请求匹配多行时,交互优先:任何仅在点击或输入后出现的内容都需要实时窗口。
-
整个屏幕或流程,没有信号。 如果目标是整个屏幕或多步流程,且上述规则均不适用,如"预览结账屏幕",则询问一个问题,提供 PNG、画廊和实时窗口选项。
-
默认。 对于单个组件且没有信号时,生成一个浅色 PNG,尺寸使用
fit。这是最快的输出,也是唯一可以自行检查的输出。然后添加一行提供其他两种输出选项。
规则 1、2 和 4 无需询问即可执行。当请求多个输出时,使用相同的配置生成所有输出。不要将选择存储在 .swiftui-preview/config.json 中:它属于请求,而非项目。
在项目中的首次使用
下面命令中的 <skill-directory> 是包含此 SKILL.md 的目录;从安装位置解析它。Claude Code 在技能加载时将其声明为技能的基础目录(${CLAUDE_SKILL_DIR});在 Codex 和其他代理中,使用读取此文件的路径。不需要特定于开发者的路径或设置。使用用户当前的项目根目录并运行只读发现:
python3 "<skill-directory>/scripts/bootstrap.py" discover --root "<project-root>"
发现过程会报告 SwiftPM 清单、Xcode 项目/工作区、SwiftUI 源文件、#Preview 的存在以及文本平台证据。它从不评估清单、运行插件、构建或修改项目。会跳过嵌套的依赖/构建目录和符号链接。缺少平台元数据表示未知,而非不支持。使用请求和发现的路径来选择预期的项目和组件。仅在真正模棱两可的情况下才询问选择;当请求的来源清晰时,多个标记本身无需强制提问。
在创建固定状态(fixture)之前,读取所选的实际源文件、其现有的 #Preview 构造以及必要的本地定义。技能代理作为第一个预览请求的一部分执行此适配;当你能安全地执行时,不要停留在指示用户手动适配的指令上。保持组件真实的视图层次结构、控件、样式和输入。提取其值视图并包含所需的本地定义,用合成值替换服务依赖项,用本地操作替换服务调用。不要用外观相似的屏幕、通用演示或捆绑的示例来替代请求的组件。这些示例仅说明固定状态(fixture)API。
将适配后的固定状态(fixture)保存在用户项目内,例如 .swiftui-preview/fixtures/StatusCard.swift。切勿将项目源代码复制到共享的已安装技能目录中。不要包含 @main、应用程序委托、生产组合根、#Preview 宏或生产服务。configure、run 和 refresh 会以文本形式扫描固定状态(fixture),并在出现 @main、#Preview 或 import UIKit(包括注释中)时拒绝它;也要将这些字符串从注释中移除。错误会列出匹配的令牌及其行号。在执行固定状态(fixture)之前检查整个文件:可执行的 Swift 代码以正常调用者权限运行;此辅助程序不是沙箱。配置哈希无法确定视觉保真度或不存在副作用。
如果显式的 UIKit 或仅 iOS 的 API 使组件与 macOS 不兼容,则使用可用的 iOS 模拟器/Xcode Canvas 工作流,或者报告确切的不支持的依赖项。通用的 macOS 宿主程序不运行 UIKit。如果模块、资源或字符串目录无法安全地适配到单文件契约中,则使用可用的项目特定工作流或说明具体的限制。不要静默链接生产应用程序、执行其生命周期或更改其构建配置。
准备好并审查了真实的固定状态(fixture)后,保存所选路径(相对于项目根目录):
python3 "<skill-directory>/scripts/bootstrap.py" configure --root "<project-root>" \
--project "Package.swift" --source "Sources/StatusCard.swift" \
--fixture ".swiftui-preview/fixtures/StatusCard.swift" --component StatusCard
--project 也接受相对的 .xcodeproj 或 .xcworkspace 目录;init 是 configure 的别名。添加 --live-fixture "<relative path>" 以注册一个单独的有状态固定状态(fixture)用于实时构建(参见 references/live.md)。版本化的 .swiftui-preview/config.json 仅存储相对路径、组件名称、平台 macos 以及所选源文件和适配后固定状态(fixture)的 SHA-256 哈希值。它会随项目一起移动,并且用户可能会提交它;除非被请求,否则不要执行任何 Git 操作。除非在检查和适配之后显式使用 configure --replace 替换,否则现有配置将被保留。所选路径和配置必须存在于根目录内,没有符号链接或路径遍历。
configure 成功后,按下一节所述使用 run 生成第一个预览。后面章节中的直接辅助程序适用于没有保存配置的一次性固定状态(fixture)。
后续预览
当配置存在时,检查它并使用 run。它会验证模式、路径和哈希值,然后调度一个已知的辅助程序,并带有固定的参数位置:
python3 "<skill-directory>/scripts/bootstrap.py" run --root "<project-root>" \
--mode render --output "/absolute/task-output/card.png" \
--width 480 --height 320 --locale en_US --theme light --frame
使用 --mode gallery 配合 .html 输出,或 --mode live 配合一个新的 .app 输出。--theme 和 --frame 仅适用于渲染;画廊包含所有变体,实时预览具有原生控件。输出父目录必须存在;与直接的 render.sh 和 gallery.sh 辅助程序不同,bootstrap 会拒绝现有输出而非替换它们;要重新渲染,请选择新文件名或先移除旧的任务拥有的工件。相对输出路径相对于项目根目录解析。--mode live 仅构建;启动和检查规则在 references/live.md 中。
更改的源文件或固定状态(fixture)哈希值会停止执行。重新检查实际组件,更新固定状态(fixture),然后使用所选路径运行 configure --replace,或者在迭代实时窗口时运行 refresh。还要检查对支持定义/资源的更改:哈希值覆盖主要源文件和自包含的固定状态(fixture),而不是整个依赖关系图。切换组件时,在替换配置之前遵循首次使用的适配工作流。保留用户对现有固定状态(fixture)的编辑。
固定状态契(fixture)约和直接辅助程序
渲染器会将一个自包含的 Swift 文件与其辅助程序一起编译。其入口点为:
import SwiftUI
@MainActor
enum PreviewFixture {
static func makeView() -> some View {
Text("Example")
}
}
当编译器支持时,辅助程序会在 Swift 6 语言模式下以严格并发编译,否则在 Swift 5 模式下编译。当从 Swift 5 项目适配的固定状态(fixture)仅因并发诊断(Sendable、Actor 隔离)而失败时,不要重写组件以满足它们:将 --swift-version 5 传递给 run 或 refresh,或为直接辅助程序设置 PREVIEW_SWIFT_VERSION=5,并说明预览是在 Swift 5 模式下构建的。
使用合成数据和固定的初始状态。对于静态渲染使用惰性操作;实时固定状态(fixture)可以实现视图局部状态更改、验证和导航。在固定状态(fixture)中包含组件所需的本地视图类型;保留真实的 SwiftUI 控件,如 Form、TextField、SecureField 和 Toggle。将凭据存储、网络调用、持久化以及生存期超出视图的工作排除在固定状态(fixture)之外;实时固定状态(fixture)可以使用短内存延迟来模拟服务。
当项目已有 #Preview 固定状态(fixture)时,重用其视图构造和输入。不要在此独立固定状态(fixture)中编译 #Preview 宏或 @main App。不要仅为渲染组件而重写项目配置。
相同的固定状态(fixture)契约适用于所有为 macOS 编译的 SwiftUI 组件。仅用于 UIKit 的组件需要 iOS 模拟器工作流。辅助程序不会自动链接任意项目模块或编译/复制资源和字符串目录。在实际可行的情况下,提取自包含的值视图。如果组件需要项目模块或 bundle,请说明此独立命令需要显式适配的构建;不要声称语言环境注入能提供缺失的翻译或资源。
渲染和检查
使用适合当前任务的路径。从此技能的位置解析技能目录,引用每个路径,并首先创建输出父目录。调用:
bash "<skill-directory>/scripts/render.sh" \
"/absolute/path/Fixture.swift" "/absolute/path/component-light.png" \
480 320 light en_US
参数为固定状态(fixture)、输出 PNG、内容宽度、内容高度、外观(light 或 dark)和语言环境标识符。宽度和高度是 1 到 4096 之间的整数,以每点一个像素渲染,或使用 fit。现有的输出文件会被原子地替换;选择任务拥有的路径。当外观比较有用时,使用 dark 和不同的文件名重复。语言环境仅设置 SwiftUI 的环境;如果测试的一部分是 RTL 布局方向,则需在固定状态(fixture)中显式选择。
选择视口。 根据组件调整其大小,而不是使用示例数字。对于全屏,使用目标窗口或设备的点大小。对于一行、卡片或控件,传递其真实容器的宽度,高度使用 fit,例如 390 fit;渲染器在每个拟合轴上测量固定状态(fixture)的理想大小,并将 render: fitted content size WxH 打印到 stderr。fit fit 适用于本征大小的控件,但基于 Spacer、maxWidth: .infinity 或 ScrollView 构建的视图在拟合轴上会收缩到最小值,因此请为这些视图指定数字。run --mode render 接受 --width fit 和 --height fit;画廊和实时需要数字,因此在那里重用报告的大小。
仅当请求或对演示有用时才附加 --frame。它会添加一个装饰性标题栏和固定内容视口外部的周围内边距:输出尺寸变为宽度 + 48 乘以高度 + 88。其交通灯是绘制的形状。它不会创建真实的窗口或验证原生窗口的 chrome。
在报告视觉结果之前,使用可用的本地图像查看器检查生成的图像。如果内容缺失或裁剪,首先检查固定状态(fixture)的布局和视口。对于需要窗口、应用程序生命周期或不可用资源的组件,请停止并解释,而不是静默启动生产应用程序。
画廊和实时窗口
另外两种输出使用相同的固定状态(fixture)契约。在生成它们之前,请完整阅读链接的参考文档;每个都有关于输出路径、启动以及哪些算作已验证的规则。
- HTML 主题画廊,当用户想要比较浅色/深色或切换装饰性边框时:使用
run --mode gallery,或直接使用scripts/gallery.sh。参见 references/gallery.md。 - 实时原生窗口,用于真实的按钮、文本输入、焦点和导航:使用
run --mode live,或直接使用scripts/live.sh。它会构建一个.app但不启动它。一个具有惰性操作的静态固定状态(fixture)在被点击时不会显示任何内容;references/live.md 解释了何时使用configure --live-fixture添加单独的有状态固定状态(fixture)。打开的窗口永远不会拾取代码更改:更新固定状态(fixture)后,refresh会在同一路径下重建应用程序,关闭旧窗口,然后重新打开它(同一参考文档)。
呈现结果
在向用户展示之前,先亲自检查每个工件,然后使用当前代理可用的任何方式呈现它。使用会话中实际可用的工具;不要假定此列表中的某个工具存在。
输出位置。 使用任务拥有的目录。当客户端只能在项目内显示文件时,写入 <project-root>/.swiftui-preview/artifacts/(先创建它),并且除非被请求,否则不要对其执行任何 Git 操作。
Claude Code。
- PNG:使用 Read 工具读取文件以检查图像。要展示它,请使用会话的文件展示或预览工具(如果存在);否则,将绝对路径作为链接提供,并且当用户要求查看时,运行
open "/absolute/path/component-light.png"。内联 Markdown 图像在终端中不会渲染。 - HTML 画廊:使用可用的浏览器或预览工具通过
file://URL 打开它,或运行open "/absolute/path/component-gallery.html"。在报告它们已验证之前,通过浏览器工具练习主题和框架控件;如果没有浏览器工具,请说明控件未被测试。 - 实时应用:当启动包含在请求中时,运行
open "/absolute/path/SwiftUI Preview.app";除非给定--no-launch,否则refresh会自行打开应用。在报告交互式行为之前验证交互。
…(原文截断)