Claude が新たにリリースした生成 UI インタラクションを再現してみた!

@op7418
中国語4 か月前 · 2026年3月15日
265K
446
63
34
815

TL;DR

この記事では、Code Pilot における Claude の生成 UI の再現について詳しく解説します。サンドボックス iframe のレンダリング、ストリーミング JSON のパース、そしてスムーズでちらつきのない体験を実現するための UX の改善など、技術的な課題に焦点を当てています。

Anthropic が最近、Claude に新しい生成 UI インタラクションをリリースしました。これにより、チャットストリーム内で概念や情報を視覚的に紹介できるようになり、プレーンテキストよりもはるかに理解しやすくなりました。 !Image 私も同様のソリューションを検討していましたが、Claude がリリースしたのを見て、作業を加速しなければと感じました。同時に、そのアプローチをリバースエンジニアリングして参考にすることもできました。Codex と Claude に集中的にプロンプトを送り続けた結果、2 日間で実際に実現できました! !https://pbs.twimg.com/amplify_video_thumb/2033112086713364480/img/EUmHCjsTK7tfnToZ.jpg この機能により、AI はチャット内でストリーミング出力とともにインタラクティブなチャートを直接描画し、生成しながらレンダリングできるようになります。以前は、AI にウェブページを作成してもらう場合、コード全体が生成されるのを待ってからレンダリングする必要があり、非常に時間がかかりました。今は違います。キャンバス上でチャートが一筆一筆描かれ、SVG ノードが次々と現れるのを見ることができます。生成プロセス自体が見事で、完了後すぐにインタラクティブに操作できます。 私の Agent プロダクト、Code Pilot で直接体験してください: https://github.com/op7418/CodePilot この記事では、その使い方、具体的な実装プロセス、注意点について紹介します。 ## どんな楽しい使い方がある? データ分析:ついに数字が意味を持つ 例えば、「米イラン紛争の1日あたりのコスト見積もり」のチャートを描くように依頼します。以前は、AI はテキストの壁を返し、数値の関係がわかりにくいものでした。今では、直接チャートを生成し、各部分の金額が一目瞭然です。テキストとチャートが一緒に出力され、必要な場所で説明し、必要な場所で描画します。 !Image 小さなツール:インタラクティブな計算機などを作る 複利計算機を作るように依頼します。スライダーをドラッグして初期金額や投資年数を変更すると、下のチャートと数値がリアルタイムで更新されます。これは静的な画像ではなく、真にインタラクティブなツールです。ローン計算、単位変換なども同様に行えます。 !Image アーキテクチャ図:プログラマーのお気に入り プロジェクトのアーキテクチャを描いたり、実装計画を可視化するように依頼できます。例えば、ここでは API から JWT 認証までの完全なフローを描くように依頼しました。機能比較、フローチャート、階層構造がすべてグラフィカルになり、テキストの説明を読むよりもはるかに速くアーキテクチャを理解できます。 !Image オンラインデータを分析する もう一つの遊び方は、GitHub リポジトリのリンクを渡すことです。データをクロールして視覚的に分析します。例えば、自分のプロジェクトアドレスである CodePilot を分析用に送信しました。Stars、Forks、技術スタック、アーキテクチャ設計、コアモジュールがすべてチャートに描かれます。プロジェクトの全体像が一目でわかり、長いテキストを読むよりはるかに優れています。 !Image インタラクティブで深い説明 最も強力な点は、モデルとの緊密な統合です。一度きりの出力ではありません。生成された図と対話し、より詳細な説明を求めることができます。例えば、ここではモンスーンと海流の関係を説明するように依頼しました。 !Image さらに詳しく知りたい場合は、海流メカニズムのボタンをクリックします。現在のモデルに自動的にコマンドを送信し、海流メカニズムの図を生成し続けます。 !Image もちろん、より複雑なインタラクションも可能です。例えば、一般的な物理や数学の公式を可視化できます。これは学生にとって非常に便利で、すべてのパラメータをスライダーや入力で制御でき、アニメーションが即座に変化します。 !Image 中国のモデルをサポート CodePilot に実装された後、これは Claude だけのものではありません。Kimi K2.5、Minimax M2.5、Anthropic のネイティブモデルもすべて正常に動作します。K2.5 が描くグラフィックは Sonnet 3.5 よりも見栄えが良く、アーキテクチャ分析も非常に詳細だと思います。この機能を使用する場合は、まず K2.5 を試すことをお勧めします。 さて、モデルのデモは以上です。実装方法に関心がなければ、CodePilot をインストールして楽しんでください。 ## どのように実装されたのか? !Image Claude のやり方 Claude.ai は公式に tool_use メカニズムを使用しています。モデルは専用のツールを呼び出して構造化されたウィジェットコンテンツを出力し、フロントエンドはツール呼び出しの入力パラメータを解析してレンダリングします。このソリューションは Claude.ai 独自のアーキテクチャでは問題なく動作します。しかし、CodePilot では 3 つの理由で機能しません。 第一に、SDK の制限です。CodePilot は Claude Agent SDK の claude_code プリセットモードを使用しており、カスタムツールを登録できません。SDK はテキストデルタストリームを公開するため、ツールレベルで拡張できません。 第二に、ストリーミング体験です。tool_use の結果は input_json_delta が完了するのを待ってからレンダリングする必要があり、インクリメンタルな HTML レンダリングをサポートしていません。コードフェンスを使用すると、HTML はテキストストリームとともに到着し、生成中にレンダリングできます。 第三に、レンダリングの分離です。Claude.ai は Shadow DOM を使用して分離しています。私たちはサンドボックス iframe を選択しました。iframe 分離はより徹底しており、完全に独立した JS 実行環境、CSP がリソースのロードを正確に制御し、スタイルの漏洩やスクリプトのエスケープがありません。 私たちのやり方 トリガー:コードフェンス モデルは特別な Markdown コードフェンスを出力してレンダリングをトリガーします: ``show-widget {"title":"training_flow","widget_code":"<svg width=\"100%\" viewBox=\"0 0 680 400\">...</svg>"} ` このフォーマットは、CodePilot の既存のコードフェンスモード(image-gen-request、batch-plan など)を再利用しており、フロントエンドのパーサーチェーンが自然にサポートします。 ![Image](https://pbs.twimg.com/media/HDcR5dyasAAtwTN?format=jpg&name=small) **レンダリング:サンドボックス iframe** 各ウィジェットは、sandbox="allow-scripts" が設定された iframe でレンダリングされます。iframe の srcdoc は注意深く構築されたレシーバーページです。CSP ポリシーは 4 つの CDN ドメインからの外部スクリプトのみを許可し、connect-src 'none' ですべてのネットワークリクエストを禁止します。コンテンツの更新は postMessage を介して受信されます。ストリーミングプレビュー段階では、スクリプトを実行せずに widget:update が送信されます。最終レンダリングでは widget:finalize が送信され、スクリプトが実行されます。ResizeObserver はコンテンツの高さの変化を監視し、postMessage を介して親ページに報告します。すべての <a> クリックはインターセプトされ、新しいウィンドウで開くために親ページに転送されます。テーマの同期は、親ページのクラス変更を監視して、ダーク/ライトモードをリアルタイムで切り替えることに依存しています。 ![Image](https://pbs.twimg.com/media/HDcR8ilbgAATB-8?format=jpg&name=small) **CSS 変数のブリッジング** これは、ウィジェットをアプリと視覚的に調和させるための鍵です。CodePilot は OKLCH 色空間の CSS 変数を使用しています。Anthropic のウィジェットデザインガイドラインは、--color-background-primary のような標準的な変数名を使用しています。ブリッジングレイヤーは、初期化時に CodePilot の変数値を iframe の :root に注入します。ガイドラインに従ってモデルが記述した CSS は、現在のテーマの色を直接使用できます。ダークモードに切り替えると、親ページがクラス変更を検出し、変数値を再計算して iframe にプッシュします。 ![Image](https://pbs.twimg.com/media/HDcR_fLaYAAL88P?format=jpg&name=small) **ストリーミングレンダリング** これは実装全体の中で最も複雑な部分です。モデルはトークンを 1 つずつ生成します。どの時点でも、受信したウィジェットコードは不完全な JSON、不完全な HTML、または不完全な <script> タグである可能性があります。処理フローは次のとおりです: 正規表現が `show-widget にマッチし、「未クローズ」と「クローズ済み」の状態を区別します。"widget_code":" の後のコンテンツを手動で特定し、1 文字ずつアンエスケープします。JSON がまだ完了していないため、JSON.parse は使用できません。未クローズの <script> タグが検出された場合、<script の前で切り詰めて、JavaScript コードが表示テキストとして表示されるのを防ぎます。120ms のデバウンスにより、iframe の更新頻度が高くなりすぎるのを防ぎます。ストリーミングコンテンツはすべてのスクリプトとイベントハンドラーを削除します。プレビュー段階ではインタラクションは必要ありません。 ![Image](https://pbs.twimg.com/media/HDcSDCGb0AAwY7J?format=jpg&name=small) **エクスペリエンスの磨き上げ:気づかれるべきでない詳細** コードや実装の観点からは、それほど複雑ではありません。複雑さはエクスペリエンスを磨き上げることにあります。エクスペリエンスに影響を与える可能性のある場所が多すぎます。ユーザーはそれらの詳細や生成プロセスに気づかないようにする必要があります。これには、各段階を異なる方法で処理する必要があります: 1. ストリーミングに見えないようにする詳細 2. 表示されるべきでないコンテンツ ![Image](https://pbs.twimg.com/media/HDcSKQLaEAABilZ?format=jpg&name=small) **テキストの消失** モデルは最初に導入テキスト(「視覚的に説明します...」)を出力し、次にウィジェットフェンスを開始します。フェンスが現れるとすぐに、先行するテキストが突然消え、ウィジェットのレンダリングが完了した後にのみ戻ります。理由は、parseAllShowWidgets() がプレーンテキストに対して空の配列を返すためです。フェンスが現れたばかりでクローズされていない場合、フェンスの前のテキストがこの関数に渡され、削除されます。 修正:フェンスの前のテキストに完了したウィジェットフェンスが含まれていない場合、解析関数をバイパスして、直接 <MessageResponse> としてレンダリングします。 **高さのジャンプ** ウィジェットのレンダリングが完了した瞬間、チャット領域全体が揺れます。iframe の初期の高さは 0px です。コンテンツが最初に実際の高さを報告するとき、400px+ になる可能性があり、CSS トランジションによりこの変化が 300ms かけて発生するため、顕著なジャンプが発生します。 修正:最初の高さ報告中に CSS トランジションを一時的に無効にして、高さを即座にスナップさせます。その後の高さ調整はスムーズなトランジションを使用します。 **ファイナライズ時のちらつき** ウィジェットがストリーミングプレビューから最終レンダリングに切り替わるとき、コンテンツがちらつきます。レシーバー iframe は、ファイナライズ中に root.innerHTML = html で DOM 全体を置き換えます。新しいコンテンツと古いコンテンツが同一(純粋な SVG ウィジェット)であっても、ブラウザは 1 フレームの再描画をトリガーします。 修正:ファイナライズ中に、最初に新しい HTML を一時的なコンテナに解析し、スクリプト要素を分離します。スクリプトなしのビジュアル HTML を現在の DOM と比較します。同じであれば、innerHTML の置き換えをスキップし、実行のためにスクリプトを追加するだけです。純粋な SVG ウィジェットは、再描画ゼロのファイナライズを実現します。 **スクロールのジャンプ** チャットは自動的に一番下にスクロールし、突然数百ピクセル戻り、また戻ります。ストリーミングが終了すると、StreamingMessage コンポーネントがアンマウントされ、MessageItem コンポーネントがマウントされます。これらは 2 つの完全に異なる React コンポーネントであり、内部の WidgetRenderer は破棄されて再構築されます。新しいインスタンスの iframe の高さは 0 から始まるため、コンテンツ領域の高さが急激に低下し、use-stick-to-bottom でスクロール調整がトリガーされます。 修正:モジュールレベルの高さキャッシュ。ウィジェットが高さを報告するたびに、widgetCode の最初の 200 文字をキーとしてキャッシュに書き込まれます。新しい WidgetRenderer インスタンスは、useState の初期化中にキャッシュから高さを読み取るため、iframe は正しい高さでレンダリングを開始し、0→実際の高さへの遷移がなくなります。 **スクリプトコードの漏洩** Chart.js を含むウィジェットがロードされると、大量の JavaScript コードが下部に表示されます。モデルが出力する <script> タグは 1 文字ずつ到着します。開始タグが到着しても終了タグがまだ到着していない場合、sanitizeForStreaming は開始タグを削除しますが、内部の JavaScript コードは裸のテキストノードとなり、表示可能なコンテンツとしてレンダリングされます。 修正:StreamingMessage で部分的なコードを抽出した後、最後の <script に対応する </script> があるかどうかを確認します。ない場合は、<script の位置で切り詰めます。ウィジェットガイドラインでは、スクリプトは最後に配置されることが指定されているため、切り詰めは視覚的なコンテンツに影響を与えません。切り詰め中は、シマーマスクが表示され、ステータスバーに「可視化にインタラクティブなアニメーションを追加しています」と表示されます。 **iframe 準備完了の競合状態** まれに、ウィジェットがまったくレンダリングされず、高さ 0px のままになることがあります。WidgetRendereruseEffect を介してメッセージイベントリスナーを登録します。ただし、iframe のレシーバースクリプトは、ロードされるとすぐに widget:ready を送信します。iframe が React エフェクトの実行よりも速くロードされた場合、widget:ready はリスナーが登録される前に送信され、iframeReady が true になることはありません。 修正:フォールバックとして、iframe 要素に onLoad コールバックを追加します。onLoad がトリガーされると、レシーバースクリプトは実行を完了しているはずであり、信頼性の高い準備完了シグナルを提供します。 **React コンポーネントツリーの安定性** フェンスが閉じた瞬間にウィジェットがちらつきます。2 つの問題が重なっています。ストリーミング中の部分的なウィジェットには React キーがなく、クローズ後に key="w-0" が取得されます。キーの変更により再マウントが発生します。また、シマーオーバーレイはラッピング <div> で実装されていたため、コンポーネントツリー構造が変更され、別の再マウントが発生しました。 修正:部分的なウィジェットに安定したキー(w-N、N は最終的なセグメント配列内の期待される位置)を割り当て、クローズ後のキーと一致させます。シマーオーバーレイを WidgetRenderer 内に移動し、showOverlay プロップを介して制御します。コンポーネントツリーは全体を通じて <WidgetRenderer key="w-N"> のままです。 ## **最後に** 生成 UI システム全体にとって、難しいのは「HTML の一部を iframe で実行すること」ではありません。それは簡単です。本当の複雑さは、ストリーミング、コンポーネントライフサイクルの切り替え、テーマ変更などの状態遷移中に、その iframe を視覚的に安定させておくことにあります。すべての「ちらつき」、「ジャンプ」、「消失」には、React のリコンシリエーション、ブラウザのレンダリングパイプライン、postMessage` のタイミングを理解する必要があります。最終的な効果は、ユーザーがチャートや図がモデルの応答に自然に散りばめられているのを見て、まるで最初からそこにあるべきだったかのように感じることです。 今日は以上です。これが役に立ったと思ったら、いいねを押すか、必要としているかもしれない友人と共有してください。

ワンクリック保存

YouMindでバイラル記事をAI深読み

ソースを保存し、的を絞った質問をし、主張を要約して、バイラル記事を再利用できるノートに変えます。すべてを1つのAIワークスペースで行えます。

YouMindを探索
クリエイターのために

あなたの Markdown をきれいな 𝕏 記事に

自分の長文を投稿するとき、画像・表・コードブロックを 𝕏 向けに整形するのは手間がかかります。YouMind は Markdown 全体を、そのまま投稿できるきれいな 𝕏 記事に変換します。

Markdown → 𝕏 を試す

解読すべきパターンをもっと

最近のバイラル記事

バイラル記事をもっと見る