UI プレビューファースト (TinyCorn Studio 標準)
ルール: すべての UI 変更は、コードに適用する前にユーザーがプレビュー画像を承認する必要があります。
ワークフロー
-
tools/preview_<名前>.swiftにオフスクリーンレンダリングスクリプトを作成します。アプリを実行する必要はなく、直接 PNG 2x をレンダリングします。 -
スクリプトを実行し、ユーザーに画像を表示します。
swift tools/preview_<名前>.swift ~/Desktop/<アプリ名>-<機能名>-Preview.png open ~/Desktop/<アプリ名>-<機能名>-Preview.png -
承認を求めます — デザインの選択肢が複数ある場合は、2〜4 つの案を並べてレンダリングし、ユーザーに選択してもらいます(A/B/C/D)。
-
ユーザーが OK した後にのみ適用します。 プレビュースクリプトも機能と一緒にコミットします。
-
ユーザーから修正依頼があった場合:スクリプトを修正し、再レンダリングし、承認されるまで繰り返します。
コードスケルトン (SwiftUI, macOS)
import AppKit
import SwiftUI
struct PreviewRoot: View {
var body: some View {
// アプリの実際のコンポーネント/色を使用して UI を模擬構築します
VStack { /* ... */ }
.frame(width: 900, height: 600)
.background(Color(nsColor: .windowBackgroundColor))
}
}
let host = NSHostingView(rootView: PreviewRoot())
host.frame = NSRect(x: 0, y: 0, width: 900, height: 600)
host.layoutSubtreeIfNeeded()
let rep = host.bitmapImageRepForCachingDisplay(in: host.bounds)!
host.cacheDisplay(in: host.bounds, to: rep)
let png = rep.representation(using: .png, properties: [:])!
let out = CommandLine.arguments.count > 1
? CommandLine.arguments[1]
: "preview.png"
try! png.write(to: URL(fileURLWithPath: out))
print("Wrote \(out)")
落とし穴 (実際に発生したもの)
- グローバル変数名を SwiftUI の modifier/view 名と重複させない (例:
border) → オーバーロードエラー。borderColorを使用してください。 - 複数のメソッド間で共通して使用するヘッダーは、同じファイル内の他の場所で宣言を重複させないでください。
- 2x レンダリング:
rep.sizeを設定するか、上記のようにbitmapImageRepForCachingDisplay+cacheDisplayを使用します。キャッシュする前にlayoutSubtreeIfNeeded()を呼び出すのを忘れないでください。 screencapture/AppleScript を使用してアプリをキャプチャしないでください — マシンにスクリーンレコーディングの権限がないことがよくあります (-1743)。オフスクリーンレンダリングが唯一確実な方法です。- ダークテーマのアプリ: アプリと同じ
Color(nsColor:)とアセットを使用して、プレビューを実際の見た目に合わせてください。 - 画像はユーザーが見つけやすいように
~/Desktop/に保存し、ファイル名は<アプリ名>-<機能名>-Preview.pngの形式にしてください。
SwiftUI 以外の場合
- Web: HTML モックアップをブラウザで開いてキャプチャするか、Playwright でスクリーンショットを取得します。
- CLI/TUI: ターミナルで ANSI レンダリングするか、色付きのテキストで説明します。
共通原則は変わりません: 画像が先、コードは後、ユーザーが承認してから適用。