初級
Claudeがリリースしたばかりの生成UIインタラクションを再現してみた!
Claudeがリリースしたばかりの生成UIインタラクションを再現しました!
Claudeがリリースしたばかりの生成UIインタラクションを再現してみた!#
一昨日、AnthropicがClaude内で生成UIに基づく新しいインタラクションをリリースしました。
チャットメッセージの流れの中で、概念や情報を視覚的に紹介することができ、プレーンテキストよりもはるかに理解しやすくなっています。

私は以前から同様のソリューションを検討していました。Claudeがリリースしたのを機に、自分の作業を加速させる必要があると感じました。
また、彼らのアプローチをリバースエンジニアリングする良い機会にもなりました。
CodexとClaudeを2日間集中的に使って、実際に実現することができました!
この機能により、AIがチャット内でインタラクティブなチャートを直接描画し、ストリーミング出力で生成しながらレンダリングすることができます。
以前は、AIにウェブページを書いてもらう場合、ページ全体のコードが生成されるのを待たなければならず、非常に時間がかかっていました。
今は違います。キャンバス上でチャートが一筆一筆描かれるのを見ることができ、SVGノードが次々と現れます。
生成プロセス自体が印象的で、完了するとすぐにインタラクションが可能です。
私のAgent製品、Code Pilotで直接体験できます:https://github.com/op7418/CodePilot
この記事では、その使い方、具体的な実装プロセス、およびいくつかの考慮事項を紹介します。
楽しい使い方は?#
データ分析:数字を理解可能に
例えば、「米国とイランの紛争の日々のコスト見積もり」のチャートを描画するように依頼します。
以前は、AIが大量のテキストブロックを出力し、数値の関係を把握することが不可能でした。
今ではチャートを直接出力し、各部分の金額を明確に示し、出力にはテキストとチャートが混在しています。必要な場所では説明し、必要な場所では描画します。

小さなツール:インタラクティブな計算機などを作成
複利計算機を作成するように依頼します。
スライダーをドラッグして初期金額、投資期間を変更すると、下のチャートと数字がリアルタイムで更新されます。
これは静的な画像ではなく、実際のインタラクティブなツールです。
ローンの計算、単位変換など、様々なものを作成できます。

アーキテクチャ図:プログラマのお気に入り
プロジェクトのアーキテクチャを描画したり、特定の実装計画を視覚化するように依頼できます。
例えば、ここではAPIからJWT認証までの完全なフローを描画するように依頼しました。
機能比較、フローチャート、階層構造など、すべてグラフィカルで、テキストの説明を読むよりもはるかに速くアーキテクチャを理解できます。

オンラインデータの分析
もう一つのトリックは、GitHubリポジトリのリンクを直接渡すと、AIがデータを取得して分析を視覚化することです。
例えば、ここでは私自身のプロジェクトアドレス、Codepilotを渡して分析を依頼しました。
スター数、フォーク数、技術スタック、アーキテクチャ設計、コアモジュールなど、すべてチャートとして描画されます。
大量のテキストを読むよりも、プロジェクトの概要を一目で把握できます。

インタラクティブで詳細な説明
最も強力な点は、モデルと非常に緊密に統合されていることで、単発の出力ではありません。
生成された図とインタラクションし、より詳細な説明を求めることができます。
例えば、ここではモンスーンと海流の関係を説明するように依頼しました。

より詳細を理解したい場合は、「海流メカニズム」ボタンをクリックできます。
現在のモデルに指示を送り、海流メカニズムの概略図を引き続き生成します。

もちろん、一般的な物理学や数学の公式を視覚化するなど、さらに複雑なインタラクションも可能です。
これは学生にとって非常に便利です。各パラメータはスライダーや入力で制御でき、アニメーションが即座に変化します。

国内モデルのサポート
Codepilotで実装した後、Claudeだけでなく使用できます。
Kimi K2.5、Minimax M2.5、およびネイティブのAnthropicモデルもすべて正常に動作します。
K2.5が描画するグラフィックはSonnet 4.6よりも美しく、アーキテクチャ分析も非常に詳細だと思います。
この機能を使用する場合は、まずK2.5を試すことをお勧めします。
さて、モデルの能力については以上です。
実装方法に興味がない場合は、Codepilotをインストールして遊んでみてください。
どのように実装されているのか?#

Claudeの方法
Claude.ai公式では
tool_use メカニズムを使用しています。モデルは専用のツールを呼び出して、構造化されたウィジェットコンテンツを出力します。
フロントエンドはツール呼び出しの
input パラメータを解析してレンダリングします。このソリューションはClaude.ai独自のアーキテクチャ内では問題なく動作します。
しかし、CodePilotに移植すると動作しません。理由は3つあります:
- SDKの制限。 CodePilotはClaude Agent SDKの
preset: '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など)を再利用しており、フロントエンドのパーサーチェーンが自然にサポートしています。
レンダリング:サンドボックス化されたiframe
各ウィジェットは
sandbox="allow-scripts" iframeでレンダリングされます。iframeの srcdoc は慎重に構築されたレシーバーページです。CSPポリシーは4つのCDNドメインからの外部スクリプトのみを許可し、
connect-src 'none' ですべてのネットワークリクエストをブロックします。コンテンツの更新は
postMessage を介して受信されます。ストリーミングプレビュー段階では、スクリプトを実行せずに widget:update が送信されます。最終レンダリングでは、スクリプトを実行するために widget:finalize が送信されます。ResizeObserver はコンテンツの高さの変化を監視し、postMessage を介して親ページに報告します。すべての
<a> クリックはインターセプトされ、新しいウィンドウで開くために親ページに転送されます。テーマの同期は、親ページのクラス変更を監視し、ダーク/ライトモードをリアルタイムで切り替えることに依存しています。

CSS変数のブリッジング
これはウィジェットを視覚的にアプリケーションに統合するための鍵です。
CodePilotはOKLCHカラースペースのCSS変数を使用しています。Anthropicのウィジェット設計ガイドラインは
--color-background-primary のような標準的な変数名を使用しています。ブリッジングレイヤーは、初期化中にCodePilotの変数値をiframeの
:root に注入します。ガイドラインに従ってモデルが記述したCSSは、現在のテーマの色を直接使用できます。ダークモードが切り替わると、親ページはクラスの変更を検出し、変数値を再計算してiframeにプッシュします。

ストリーミングレンダリング
これは実装全体で最も複雑な部分です。
モデルはトークンごとに生成します。任意の時点で受信するウィジェットコードは、不完全なJSON、不完全なHTML、または不完全な
<script> タグである可能性があります。処理フローは次のようになります:
正規表現が
```show-widget に一致し、「未閉じ」状態と「閉じ」状態を区別します。"widget_code":" の後のコンテンツを手動で見つけ、文字ごとにエスケープ解除します。JSONが完了していないため、JSON.parse は使用できません。未閉じの
<script> タグが検出された場合、JavaScriptコードが可視テキストとして表示されるのを避けるために、<script の前で切り捨てます。120msのデバウンスにより、iframeが頻繁に更新されるのを防ぎます。
ストリーミングコンテンツはすべてのスクリプトとイベントハンドラーを除去します。プレビュー段階ではインタラクションは必要ありません。

体験の磨き上げ:気づかれるべきではない詳細
実際、コードや実装計画を見ると、複雑ではありません。複雑なのは体験を磨き上げることです。
体験に影響を与える可能性のある場所は非常に多く、ユーザーにそれらの詳細や生成プロセスに気づかせないようにする必要があります。これには各段階で異なる方法で処理することが必要です:
- 非ストリーミングに見える詳細
- 表示されるべきではないコンテンツ

テキストの消失
モデルはまずいくつかの紹介テキスト(「視覚的に説明します...」)を出力し、その後ウィジェットフェンスの出力を開始します。
フェンスが現れるとすぐに、前のテキストが突然消え、ウィジェットのレンダリングが完了した後にのみ戻ります。
理由は、
parseAllShowWidgets() がプレーンテキストに対して空の配列を返すためです。フェンスが最初に現れたがまだ閉じられていない場合、フェンス前のテキストがこの関数に渡され、失われます。修正:フェンス前のテキストが検出され、完了したウィジェットフェンスを含まない場合、解析関数をバイパスして直接
<MessageResponse> としてレンダリングします。高さのジャンプ
ウィジェットのレンダリングが完了した瞬間、チャットエリア全体が揺れます。
iframeの初期高さは0pxです。
コンテンツが最初に実際の高さを報告するとき、400px以上になる可能性があります。
CSSトランジションにより、この変化が300msかけて発生し、目立つアニメーションジャンプが生じます。
修正:最初の高さ報告時にCSSトランジションを一時的に無効にし、高さを瞬時に設定します。その後の高さの微調整はスムーズなトランジションを使用します。
ファイナライズ時のちらつき
ウィジェットがストリーミングプレビューから最終レンダリングに切り替わるとき、コンテンツがちらつきます。
レシーバーiframeはファイナライズ中に
root.innerHTML = html を実行し、DOM全体を置き換えます。古いコンテンツと新しいコンテンツが完全に同じ(純粋なSVGウィジェット)であっても、ブラウザはフレームの再描画をトリガーします。修正:ファイナライズ中、まず新しい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> タグは、ストリーミング中に文字ごとに到着します。<script> 開始タグが到着したが </script> がまだ到着していない場合、sanitizeForStreaming は開始タグを除去しますが、タグ内のJavaScriptコードは裸のテキストノードになり、ブラウザによって可視コンテンツとしてレンダリングされます。修正:
StreamingMessage で部分コードを抽出した後、最後の <script に対応する </script> があるかどうかを検出します。ない場合、<script の位置で切り捨てます。ウィジェットガイドラインではスクリプトが最後に来ると指定されているため、切り捨ては視覚コンテンツに影響しません。切り捨て中は、シマーオーバーレイを表示し、ステータスバーに「視覚化のためのインタラクティブなアニメーションを追加中...」と表示します。**iframeの準備完了競合