Cursor BYOK フロントエンド
apps/desktop に既存のコンポーネントとテーマシステムを基に構築します。Tauri HTTP バウンダリを維持し、フロントエンド管理機能は /__byok-api__/api を呼び出し、IPC ビジネス API を追加しないでください。
デスクトップ HTTP 通信
- Rust 管理サーバーをデスクトップ UI の唯一の HTTP オリジンとして扱います。メイン WebView とブラウザで開かれた詳細ページは
http://127.0.0.1:<動的ポート>/__byok-api__/を使用する必要があります。tauri://localhostからメイン UI をロードしたり、2 番目のフロントエンドオリジンを公開しないでください。 - すべてのフロントエンド管理リクエストは
/__byok-api__/apiを基準にします。apiOriginを検出、注入、永続化、または渡さないでください。また、固定ポートから管理 URL を構築しないでください。 /__byok-api__/名前空間はこのバウンダリ用に予約します:/__byok-api__/api/*は Rust 管理 API によってローカルで処理されます。- その他の
/__byok-api__/*パスはフロントエンドドキュメント、アセット、モジュール、開発リソースです。
- プロダクションでは、Rust サーバーを通じて Tauri の
frontendDistによって埋め込まれたフロントエンドを提供します。Tauri のアセットリゾルバを再利用し、フロントエンドリソースの 2 番目のセットをバンドルまたはコピーしないでください。 - 開発時は、Rust サーバーに非 API の
/__byok-api__/*リクエストを Vite にリバースプロキシさせます。リクエストパスとクエリ文字列を保持します。管理 API リクエストを Rust にプロキシするように Vite を構成しないでください。 - Vite のベースパスを
/__byok-api__/に設定し、生成されたアセット、モジュールインポート、開発クライアント URL が予約された名前空間の下に留まるようにします。 - 開発用 Vite サーバーを、Rust プロキシターゲットと互換性のある明示的なループバックアドレスにバインドします。
localhostが同じ IP ファミリに解決されることに依存しないでください。 - 外部の詳細ページは、通常のループバック HTTP URL とハッシュルートで開きます。ページと API は同一オリジンであるため、Tauri URL 回避策、API オリジンクエリパラメータ、または CORS 依存のブラウザフローを追加しないでください。
- Tauri コマンドは、ターミナルの起動、トレイ統合、OS アクションなどのネイティブデスクトップ機能にのみ使用します。HTTP ビジネス API の IPC ミラーを作成しないでください。
コンポーネント境界
- Provider、Model、Call、Usage、および永続化された設定の状態は、プレゼンテーションコンポーネントの外部に保持します。
- コンポーネントはビジネススナップショットを受け取り、アクションをディスパッチします。フォーカス、開閉状態、ドラッグ状態、入力ドラフトなどの一時的な UI 動作には、ローカルの React 状態を使用できます。
- ページ、レイアウト、チャート、フォーム、再利用可能なコントロールは、焦点を絞ったコンポーネントに分割します。機能セット全体を
App.tsxに集めないでください。 App.tsxはルート構成のルートとして維持します。
ページレイアウト
- すべてのフルハイトページは
components/layout/PageLayout.tsxで構築します。グリッド行を使用してページシェルを手動で作成しないでください。 - 作成、インポート、エクスポート、フィルタリング、ソート、表示オプションなどのページレベルのコマンドとコントロールは、共有のページアクション領域
PageActionsに配置します。編集や削除など、特定のリスト項目を対象とする操作のみをその項目の行内に保持します。ページコンテンツ内に重複するアクションバーやパネルヘッダーコントロールを追加しないでください。 - データテーブルまたはフラットデータリストの上にタイトル領域をレンダリングしないでください。ページタイトルをコンテキストとして使用し、列ラベルをテーブルヘッダーに保持し、ページレベルのコントロールを
PageActionsに配置し、テーブルをタイトルのないCardでラップします。グループ化された親子ビューで親データを識別する階層ラベルはテーブルタイトルではありません。 - メインのスクロールビューポートはフルハイトで
position: relativeに保ちます。ページタイトルとアクション領域はその上に絶対配置します。 - 絶対配置されたタイトル/アクション領域は、スクロールコンテンツの
padding-topで確保します。スクロールビューポート自体を短縮またはオフセットすることでは決して確保しないでください。 - メニュー Card は固定し、コンテンツ領域の
margin-leftで確保します。Header、メニュー、コンテンツをフローグリッドに配置しないでください。 - 子またはセクションのパディングを使用して外部スペースを作成しないでください。兄弟間にはフレックス/グリッドギャップを使用し、コンテナ境界ではマージンまたは固定オフセットを使用します。スクロールコンテンツの上部パディングは、絶対配置されたタイトル/アクションオーバーレイのためだけに予約されています。
- 次のインバリアントを持つ垂直フレックスレイアウトを使用します:
- Header:
flex: 0 0 auto - Footer:
flex: 0 0 auto - コンテンツ:
flex: 1 1 auto、min-height: 0、overflow: hidden
- Header:
- Header と Footer はスクロールビューポートの外側に保ちます。これらはコンテンツとともに拡大または縮小してはなりません。
- macOS、Windows、Linux のウィンドウコーナーセーフエリア変数は、ネイティブの角丸に入ることができるエッジクロームにのみ適用します。
- macOS の信号機スペースと上部の固定ドラッグストリップには、インタラクティブなコントロールを配置しないでください。
- 装飾的なブランドテキストや説明コピーは、ユーザーのアクションや状態解釈に役立たない場合は避けてください。
タイポグラフィ
- コンポーネントスタイルは SCSS で記述し、すべての
font-sizeはsrc/styles/_typography.scssから取得します。SCSS、React スタイル、チャートオプションにフォントサイズをハードコードしないでください。 - 通常の UI テキストおよび通常のタイトル(タイトルバー、パネル、カード、セクション見出しを含む)には
baseを使用します。要素がh1やh2であるという理由だけでフォントを大きくしないでください。 xsは二次情報にのみ使用します。lgは明示的に特大の表示タイトル用に予約します。ルーチンの見出しには使用しないでください。- スケールを変更した後は、数値のフォントサイズだけでなく、すべてのタイポグラフィトークンの使用箇所を監査します。
スクロールと仮想化
- 垂直スクロールするページコンテンツには
components/layout/VirtualPage.tsxを使用します。 - 拡大する可能性のあるデータコレクションとメニューには
components/virtual/VirtualList.tsxを使用します。 ScrollAreaは、仮想スクロール実装が所有する低レベルのプリミティブとして扱います。ページやレイアウトでScrollAreaを直接インポートしないでください。- スクロールエッジシャドウには、グローバルな
scroll-shadow-topおよびscroll-shadow-bottomマスクを使用します。スクロールコンテンツのインセットが絶対オーバーレイを予約する場合、上部シャドウの距離は、そのpadding-topと同じ CSS 変数から取得します。 - リスト以外のページは、測定可能なトップレベルの仮想セクションの短いシーケンスとしてモデル化します。
- ドキュメントスクロール、ネイティブページの
overflow: auto、またはネストされた垂直スクロールコンテナを使用しないでください。 - 仮想ビューポートのすべてのフレックス/グリッド祖先が
min-height: 0を持ち、ビューポート自体に明示的な境界高さがあることを確認します。
ルーティングとページ
- Tauri 互換性のために
HashRouterを使用します。 /を統計ダッシュボードとして維持し、すべてのプライマリページは共有のAppLayoutの永続メニューからアクセスできるようにします。- プライマリページにはトップレベルルートを使用します:
/、/providers、/models、/calls、/settings。 - 個別の Home シェルと Settings シェルを作成したり、Home 切り替えコントロールを追加したりしないでください。
- ルートコンテンツはウィンドウの Header および Footer から独立させます。
チャート
components/charts/EChart.tsxを通じて ECharts を使用します。- 必要な ECharts Core チャート、コンポーネント、レンダラーのみを登録します。
- チャートコンポーネントは宣言的に保ちます。ドメインデータを受け取り、データを取得または永続化せずにオプションを構築します。
ResizeObserverでチャートをレイアウトコンテナに合わせてリサイズし、アンマウント時にインスタンスを破棄します。
検証
apps/desktop から実行:
npm run check
npm run tauri:build -- --debug --no-bundle
Header と Footer が固定されたままであること、仮想コンテンツビューポートのみがスクロールすること、設定ナビゲーションが最小ウィンドウサイズでも到達可能であること、macOS コントロールが角丸および信号機のセーフでない領域の外側にあることを確認します。