argent-lens について
argent-lensは、アプリの画面を反復的に設計するチーム向けのレビュー用スキルです。実装済みの案をシミュレーター画面に結び付けて表示し、選択やコメントを集めます。
主な機能
- 01実装済みデザイン案の提示
- 02シミュレーター画面の横にスクリーンショットを表示
- 03提案を画面要素にアンカー
- 04選択結果とコメントの収集
活用シーン
- 01検索フィールドのデザイン案の比較
- 02主要アクションボタン案のレビュー
- 03画面要素ごとのフィードバック収集
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ software-mansion/ argent --skill argent-lensインストール後、Claude Code で/argent-lensを入力して読み込まれたか確認します。
詳細
前提条件:機能フラグ。 このワークフローは
argent-lensフラグによって制御されており、デフォルトではオフです。使用する前に、argent enable argent-lensを一度実行してください。propose_variant/await_user_selectionが見つからないという結果になった場合は、フラグがオフです。有効にしてから再試行してください。
1. 概要
複数のデザイン候補を実装し、それぞれがデバイス上で動作しているところをキャプチャして、propose_variant で提示します。提案した各要素は、Argent Lens ウィンドウ(自動的に開くネイティブウィンドウ)内で、ライブシミュレーターストリームの横にフローティングカードとして表示され、実際の要素と細い線でつながります。ユーザーは要素ごとに選択し、必要に応じて要素に自由形式のコメントをピン留めしてから、Complete selection を押します。await_user_selection は、ユーザーの決定を返す唯一のブロッキング呼び出しです。
鉄則:1 つのバリアントにつき、実物の異なるスクリーンショットを 1 枚用意します。 提案が役立つのは、previewImage に、そのバリアントをデバイス上で実際にレンダリングし、適用した後にキャプチャした画像が指定されている場合だけです。実際にビルドして画面で確認していないバリアントを提案してはいけません。また、2 つのバリアントに同じファイルパスを指定してはいけません。2 つのキャプチャがバイト単位で同一になる場合、実際には何も変更しておらず、Argent Lens には同じサムネイルが並ぶだけです。すべての要素について、バリアントごとに「計画 → ビルド → 画面遷移 → スクリーンショット → 提案」を繰り返してから、最後に 1 回だけ待機します。
2. ツール
| ツール | ブロッキング? | 目的 |
|---|---|---|
propose_variant | いいえ | 1 つの要素に 1 つのバリアントを登録します。バリアントごとに 1 回呼び出します。作業を続けてください。 |
await_user_selection | はい | すべてのバリアントを登録した後に 1 回だけ呼び出します。ユーザーが完了するまで待機します。 |
propose_variant のパラメーター:element(人が読める名前)、省略可能な match({ by: "text"|"label"|"identifier"|"role", value })、省略可能な udid(バリアントをキャプチャしたデバイスの ID)、および variant({ name, summary, code?, filePath?, previewImage?, frame? })。同じ element で繰り返し呼び出すと、その要素にバリアントが追加されます。異なる element の値を指定すると、別々のカードが作成されます。
必ず udid を指定してください(スクリーンショットを撮影し、その説明に使ったものと同じシミュレーター/エミュレーターの ID)。プレビューウィンドウでは、そのデバイスの画面が直接ストリーミングされるため、ユーザーがシミュレーターを選ぶ必要はありません。ラウンド内の最初の propose_variant で設定してください。以降の呼び出しでは省略できます(最後に指定した値が使用されます)。
3. ワークフロー
まずシミュレーター/エミュレーターを解決します(argent-ios-simulator-setup/argent-android-emulator-setup)。React Native の場合は、アプリを実行してバンドルを再読み込みするために argent-react-native-app-workflow も使用します。Argent は、ユーザーの画面上に自動的に開くネイティブプレビューウィンドウに、登録したバリアントを表示します。自分で何かを開いたり表示したりする必要はありません。バリアントを登録して await_user_selection を呼び出すだけで、ウィンドウが自動的に表示されます。
ステップ 0:バリアントを計画する
コードに手を加える前に、デザインを変更する要素と、それぞれに用意する異なるバリアントを具体的に決めます。書き出しておきます(例:「検索フィールド:塗りつぶし/枠線/ピル型」、「プライマリ CTA:単色/グラデーション」)。各バリアントは、単独で適用、スクリーンショット撮影、復元できる、自己完結した変更でなければなりません。曖昧だったり重なり合ったりするバリアントを提案しても役に立ちません。
Step 1: 正確なマッチャーを取得する
各要素について、その要素が表示される画面で describe(RN の場合は debugger-component-tree)を実行し、正確な label / identifier / role を確認します。それを match として渡すと、フローティングカードのコネクターが適切な要素にアンカーされます。
- 安定した testID / accessibilityIdentifier →
{ by: "identifier", value: "search-input" }(最も信頼性が高い) - 正確な a11y ラベル →
{ by: "label", value: "Search" } - それ以外 →
{ by: "text", value: "Search" }(部分一致。matchを省略した場合のデフォルト)
match を省略すると { by: "text", value: element } がデフォルトになります。要素の表示テキストが一意の場合に限り、これで問題ありません。
Step 2: 各バリアントについて、実装 → 画面遷移 → スクリーンショット → 提案を行う
各要素のすべてのバリアントについて、以下を繰り返します。
- バリアントを実装する。 コードでそのバリアントを実装します。
- デバイスに適用する。 実行中のアプリにこのバリアントを表示するため、RN バンドルを再読み込み(
debugger-reload-metro)するか、必要に応じて再ビルドします。 - その画面に遷移する。
argent-device-interactでアプリを操作し、要素が表示される画面に移動します。要素が実際に画面上に表示されていなければ、スクリーンショットに意味はありません。 - スクリーンショットを撮る。
screenshotを呼び出し、返されたファイルパスをそのままvariant.previewImageに渡します。スクリーンショットを手作業で切り抜いたり、サイズ変更、再エンコード、別のフォルダーへのコピーをしたりしないでください(例:crop.pyで/tmp/variants/に出力する)。プレビューウィンドウ自体が切り抜きを行うため、切り抜きを重ねると、「プレビューなし」となり、サーバーが配信できない場所に画像が書き込まれてしまいます。画面全体をキャプチャしてください。プレビューウィンドウはvariant.frame(手順 5)を使って切り抜きます。取得したパスは新しいファイルでなければなりません。デバイスがフリーズした、またはバリアントが適用されていない疑いがある場合(前回のキャプチャと見た目に変化がない場合)、提案する前に前回のパスと比較(shasum -a 256)してください。キャプチャがバイト単位で同一なら、バリアントはまだ画面に表示されていません。提案は行わず、先に問題を解決してください。 - 提案する。
propose_variantを呼び出し、element、match、udid(キャプチャに使用したデバイス)を渡し、variant.previewImageにスクリーンショットのパスを設定します。このツールは切り抜きフレームを自動でキャプチャします。提案時にデバイスの情報を取得して要素をマッチングするため、各サムネイルはそれぞれの現在のレイアウトに合わせて切り抜かれます。ただし、propose_variantの呼び出し時にバリアントが画面に表示されている必要があります(スクリーンショットを撮った直後、元に戻す前に提案してください)。variant.frame(このバリアントでdescribeを実行して取得した、マッチしたノードの 0~1 の範囲に正規化された{x, y, width, height})を渡すと、自動キャプチャを上書きできます。これは、提案時に要素を画面に表示したままにできない場合に便利です。必要に応じてsummary(変更内容とその理由)とcode/filePathを追加します。 - 元に戻す。 次のバリアントをビルドする前に、バリアントの変更を元に戻します。同時に画面に表示できるバリアントは 1 つだけです。作業を続けてください。
propose_variantは処理をブロックしません。
previewImage には、ローカルのスクリーンショットパス(OS の一時ディレクトリまたは作業ディレクトリから配信)、http(s) URL、または data: URI を指定できます。実際に実行中のバリアントを撮影したローカルのスクリーンショットを強く推奨します。
Step 3: 人間の判断を待つ(1 回のみ)
すべての要素について、すべてのバリアントを準備した後、await_user_selection を 1 回だけ呼び出します。戻り値は以下のとおりです。
{ status: "completed", selections: [{ element, chosenVariant, comment? }], unselected, annotations: [{ target, match, comment }], globalComment }:各要素にchosenVariantを適用し、unselectedにある要素はスキップします。各annotationsエントリー(人間が要素にピン留めしたインスペクターのコメント)とglobalCommentは変更依頼として扱います。{ status: "pending", proposedElements }:timeoutSecondsが経過したことを示します。エラーではありません。提案は引き続き有効です。もう一度await_user_selectionを呼び出します。{ status: "no_proposals" }:propose_variantを呼び出す前に実行されています。先にバリアントを準備してください。
ステップ 4:結果を適用する
選択された各要素に選ばれたバリアントを実装し、すべての注釈/コメントに対応して、適用した内容とスキップした内容を報告します。人間がコメントを残したものの、バリアントをスキップしていた場合も、そのコメントは重要です。対応してください。
4. ルール
- 要素ごとに少なくとも 2 つのバリアントを用意する。 選択肢には代替案が必要です。提案する各要素について、少なくとも 2 回
propose_variantを呼び出して、異なるバリアントを用意してください。要素のデザイン案が 1 つしかない場合は、実際の代替案を作成するか、その要素を提案しないでください。バリアントが 1 つだけでは選択肢になりません。 - 提案する前にビルドする。 すべての
previewImageは、デバイス上で実際に動作しているそのバリアントのスクリーンショットでなければなりません。モックアップや推測に基づくものは提案しないでください。 - バリアントごとに異なるスクリーンショットを用意する。 2 つのバリアントで同じ
previewImageのパスを使い回したり、内容が同一の画像を別々のパスで撮影したりすると、Argent Lens の目的が損なわれます。視覚的に異なる画像を撮影できない場合(たとえば、アプリが読み取り専用、アクセシビリティに問題があり操作できない、バンドルがホットリロードに対応していない場合)は、重複した案を用意せず、停止してユーザーに伝えてください。 - ブロッキング呼び出しは 1 回。
propose_variantはブロックしません。自由に提案をステージングしてください。待機する唯一の呼び出しはawait_user_selectionで、最後に 1 回だけ呼び出します。 - アンカーを正確に指定する。
describeからマッチャーを取得してください。matchが誤っていると、カードが別の要素を指したり、アンカーなしで表示されたりします。 - 一度に画面に表示するバリアントは 1 つだけ。 適用 → スクリーンショット撮影 → 元に戻す、の順にして、次のバリアントの画像に影響しないようにします。
pendingは通常の状態です。pendingの場合はそのまま再度待機してください。提案はタイムアウト後も保持されます。- 再提案すると新しいラウンドが始まる。 ラウンドが消費された後に
propose_variantを呼び出すと、ラウンド N+1 が始まり、前のラウンドの要素はクリアされます。ラウンドごとに一式すべてをステージングしてください。
5. 例
describe { udid } # read exact label/identifier
propose_variant { element: "Search field",
match: { by: "identifier", value: "search-input" },
variant: { name: "Outlined", summary: "1pt border, transparent fill",
previewImage: "/var/folders/.../search-outlined.png" } }
propose_variant { element: "Search field",
match: { by: "identifier", value: "search-input" },
variant: { name: "Pill", summary: "Fully rounded, filled grey",
previewImage: "/var/folders/.../search-pill.png" } }
propose_variant { element: "Primary CTA",
match: { by: "label", value: "Get started" },
variant: { name: "Gradient", summary: "Accent gradient fill",
previewImage: "/var/folders/.../cta-gradient.png" } }
await_user_selection {} # ONE blocking call → human picks
→ { status: "completed",
selections: [ { element: "Search field", chosenVariant: { name: "Pill" } },
{ element: "Primary CTA", chosenVariant: { name: "Gradient" } } ],
annotations: [ { target: "Tab bar", comment: "raise contrast" } ] }
# → apply Pill + Gradient, and raise tab-bar contrast.