From 2dad760a23ea9260c6771448cc3b818a9f04e5c0 Mon Sep 17 00:00:00 2001 From: Nao Date: Mon, 12 Jan 2026 13:43:00 +0900 Subject: [PATCH 1/3] =?UTF-8?q?docs:=20=E3=83=98=E3=83=AB=E3=83=97?= =?UTF-8?q?=E3=82=AC=E3=82=A4=E3=83=89=E3=82=92=E6=9C=80=E6=96=B0=E3=81=AE?= =?UTF-8?q?=E6=A9=9F=E8=83=BD=E3=81=AB=E5=90=88=E3=82=8F=E3=81=9B=E3=81=A6?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AIアシスタント機能セクションを削除(ターミナルから直接起動する仕様に変更) - キャラクター設定セクションを追加(表示キャラ/読み上げキャラの設定方法) - VS Code Dark の名称を削除し「ダークテーマ」に統一 - アイコンモードの説明を「負荷軽減」目的に修正 - キャラ選択がアイコンから行える旨を追記 - CURRENT_FEATURES.md を新規作成(機能一覧の参照用) Co-authored-by: Gemini --- CURRENT_FEATURES.md | 71 +++++++++++ README.md | 217 +++++++++++++++++++-------------- src/index.html | 290 ++++++++++++++++---------------------------- 3 files changed, 303 insertions(+), 275 deletions(-) create mode 100644 CURRENT_FEATURES.md diff --git a/CURRENT_FEATURES.md b/CURRENT_FEATURES.md new file mode 100644 index 0000000..b9a97a6 --- /dev/null +++ b/CURRENT_FEATURES.md @@ -0,0 +1,71 @@ +# KawAIi Code - Current Feature Overview (v2026.1) + +> ターミナル + VRMキャラクター + 音声合成のデスクトップアプリ + +## 1. ターミナルシステム (Terminal System) + +強力なターミナルエミュレーションとウィンドウ管理機能を提供します。 + +- **コア機能**: xterm.jsベースのフル機能ターミナル +- **マルチタブ管理**: + - `+` ボタンで新しいタブを作成可能 (最大10個) + - タブごとの独立したプロセス管理 +- **画面分割 (Split Panes)**: + - タブ内での画面分割に対応(左右・上下) + - コンテキストメニュー(右クリック)から操作 + - 分割されたペインのリサイズ +- **ウィンドウ制御**: + - アプリヘッダー部分でのドラッグ移動 (Draggable Region) + - ターミナル表示/非表示のトグルスイッチ + +## 2. キャラクターシステム (Character System) + +VRMモデルを使用した3Dアバター表示とインタラクション機能。 + +- **VRM対応**: + - VRM 0.x および VRM 1.0 形式に対応 + - ドラッグ&ドロップまたはファイル選択でのインポート +- **表示モード (Display Modes)**: + - **Single**: 1体のキャラクターを大きく表示 + - **Icon**: 3D描画を抑えて動作を軽くするモード + - **Multi**: 登録済みキャラクターを並べて表示(複数同時表示) +- **表示キャラ / 読み上げキャラ**: + - 画面上のアイコンから切り替え可能 + - 表示と読み上げで別々のキャラを設定可能 +- **リアクション**: + - ターミナル出力に基づいた感情表現 (Joy, Sorrow, Angry, Fun, Neutral) + - 待機モーション (Blink, LookAround) + - リップシンク (音声再生時の口パク) + +## 3. テーマ & デザイン (Theme & Design) + +作業環境をカスタマイズするための広範なテーマ機能。 + +- **カラープリセット**: + - Standard (Orange), Pink, Blue, Green, Purple +- **ダークテーマ**: + - エンジニア向けのダークモード + - ターミナル背景と一体化した配色 +- **壁紙システム**: + - カスタム画像のアップロード (JPG/PNG) + - テーマ連動 (ダークテーマ時は単色背景に自動切り替え) + +## 4. 音声合成機能 (Voice Synthesis) + +キャラクターに声を与えるマルチエンジン対応。 + +- **ローカルエンジン**: + - **AivisSpeech**: 自動連携 (Port 10101) + - **VOICEVOX**: 自動連携 (Port 50021) +- **クラウドエンジン**: + - **Aivis Cloud API**: サーバーサイド音声合成(APIキー使用) +- **設定項目**: + - エンジン切替、話者選択、音量、読み上げ速度 + - テスト発話機能 + +## 5. 設定・管理 (Configuration) + +- **設定モーダル**: + - **表示設定**: キャラクター表示モード切替 + - **キャラクター設定**: VRM追加、プロンプト編集、音声設定 + - **テーマ設定**: テーマ・壁紙変更 diff --git a/README.md b/README.md index 49d0b7d..b598914 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # 🎀 KawAIi Code -Claude CodeとアニメキャラクターVRMが対話するデスクトップアプリケーションです。美しいカワイイデザインで、Claude Codeを直感的に操作できます。macOSとWindowsの両方で動作するクロスプラットフォーム設計です。 +Claude +CodeとアニメキャラクターVRMが対話するデスクトップアプリケーションです。美しいカワイイデザインで、Claude +Codeを直感的に操作できます。macOSとWindowsの両方で動作するクロスプラットフォーム設計です。 ![KawAIi Code](https://img.shields.io/badge/status-ready_for_distribution-brightgreen) ![Platform](https://img.shields.io/badge/platform-macOS%20%7C%20Windows-blue) @@ -9,16 +11,19 @@ Claude CodeとアニメキャラクターVRMが対話するデスクトップア ## ✨ 特徴 - 🎀 **カワイイデザイン**: 3D VRMキャラクターとアニメUI -- 💬 **AIアシスタント選択**: Claude Code、Claude Code (Dangerous)、Gemini CLI、OpenAI Codexから選択可能 -- 🖥️ **デスクトップアプリ**: Electronベースのネイティブアプリ +- 💬 **AIアシスタント連携**: Claude Code, Gemini CLI, OpenAI + Codex等のCLIツールとシームレスに統合 +- 🖥️ **強力なターミナル**: xterm.jsベース、マルチタブ、画面分割(Split + Panes)、ドラッグ移動対応 - 🔄 **リアルタイム応答**: AIアシスタントとのライブ通信 -- 🎨 **3D VRMビューワー**: Three.js/VRM対応の3Dキャラクター表示 -- 🗣️ **音声合成**: AivisSpeech連携による音声読み上げ -- 🎭 **キャラクターアニメーション**: アイドルアニメーション・LipSync機能 -- 📝 **動的プロンプト管理**: AI起動時にキャラクター設定やプロジェクト固有の指示を`.md`ファイルとして自動生成・更新(Claudeはホームディレクトリ、Geminiは作業ディレクトリ) -- 🖼️ **カスタム壁紙**: 時間帯別デフォルト壁紙・ユーザー壁紙アップロード対応 -- ⚙️ **設定管理**: キャラクター変更・作業ディレクトリ設定・プロジェクト固有設定 -- 🔧 **モジュラー設計**: リファクタリング済みの保守しやすいコード構造 +- 🎨 **カスタマイズ**: VS Code + Darkなどのテーマ切り替え、カスタム壁紙、VRMキャラクター変更 +- 🗣️ **音声合成**: AivisSpeech/VOICEVOX (ローカル)、Aivis Cloud API (クラウド) + 対応 +- 🎭 **キャラクターアニメーション**: アイドルアニメーション・LipSync・感情表現 +- 📝 **動的プロンプト管理**: + プロジェクトごとに作業ディレクトリ(CWD)を設定し、`.md`コンテキストファイルを生成・管理 +- ⚙️ **設定管理**: GUIによる直感的な設定変更 ## 📥 インストール(ユーザー向け) @@ -31,6 +36,7 @@ Claude CodeとアニメキャラクターVRMが対話するデスクトップア 以下から使用したいAIアシスタントをインストールしてください: **Claude Code(推奨)** + ```bash # Claude Code(Anthropic公式CLI)をインストール # 公式サイト: https://claude.ai/code @@ -41,9 +47,12 @@ brew install anthropics/claude/claude claude --version ``` -Windows環境では、公式インストーラ(https://claude.ai/code)から `claude.exe` を取得するか、`choco install claude` や `scoop install claude` で PATH に追加してください。 +Windows環境では、公式インストーラ(https://claude.ai/code)から `claude.exe` +を取得するか、`choco install claude` や `scoop install claude` で PATH +に追加してください。 **Gemini CLI** + ```bash # Gemini CLI(Google公式CLI)をインストール # 公式サイト: https://github.com/gemini-cli/gemini-cli @@ -54,6 +63,7 @@ gemini --version ``` **OpenAI Codex** + ```bash # OpenAI Codex(OpenAI公式CLI)をインストール # npmを使用 @@ -66,37 +76,51 @@ brew install codex codex --version ``` -※ 設定画面で使用するAIアシスタントを選択できます。複数インストールすることも可能です。 +※ +設定画面で使用するAIアシスタントを選択できます。複数インストールすることも可能です。 #### 2. 音声機能(オプション) + 音声読み上げを使用する場合、以下のいずれかが必要: -- **AivisSpeech Engine** (無料・ローカル): [公式サイト](https://aivis-project.com/)からダウンロード -- **VOICEVOX** (無料・ローカル): [公式サイト](https://voicevox.hiroshiba.jp/)からダウンロード + +- **AivisSpeech Engine** (無料・ローカル): + [公式サイト](https://aivis-project.com/)からダウンロード +- **VOICEVOX** (無料・ローカル): + [公式サイト](https://voicevox.hiroshiba.jp/)からダウンロード - **Aivis Cloud API** (有料・クラウド): APIキーを取得して設定画面で設定 ### 配布版ダウンロード -1. GoogleドライブまたはGitHub Releasesから最新の`KawAIi-Code-1.0.0-arm64.dmg`をダウンロード -2. **DMGファイルをマウント** +1. GoogleドライブまたはGitHub + Releasesから最新の`KawAIi-Code-1.0.0-arm64.dmg`をダウンロード + +2. **DMGファイルをマウント**\ ダウンロードした`.dmg`ファイルをダブルクリックしてマウント -3. **アプリをインストール** +3. **アプリをインストール**\ マウントされたディスクイメージ内の`KawAIi Code.app`をApplicationsフォルダにドラッグ&ドロップ -4. **起動** +4. **起動**\ アプリをダブルクリックして起動 #### Windows版インストール -1. GitHub Releasesから最新の `KawAIi-Code--win-x64.exe`(NSISインストーラ) をダウンロード -2. ダウンロードした `.exe` をダブルクリックし、SmartScreen の警告が出た場合は「詳細情報」→「実行」を選択 + +1. GitHub Releasesから最新の + `KawAIi-Code--win-x64.exe`(NSISインストーラ) をダウンロード +2. ダウンロードした `.exe` をダブルクリックし、SmartScreen + の警告が出た場合は「詳細情報」→「実行」を選択 3. インストーラの指示に従ってインストール(ショートカット作成・スタートメニュー登録など) -4. インストール完了後、スタートメニューまたはデスクトップアイコンから KawAIi Code を起動 +4. インストール完了後、スタートメニューまたはデスクトップアイコンから KawAIi + Code を起動 ### ✨ 署名付きビルドについて + このアプリは署名付きでビルドされているため、macOSの標準的なDMGインストーラーから直接インストールできます。従来の隔離属性削除手順は不要になりました。 ### システム要件 -- **対応OS**: Windows 10/11 64-bit、macOS 10.15 (Catalina) 以降、Linux x64 (AppImage) + +- **対応OS**: Windows 10/11 64-bit、macOS 10.15 (Catalina) 以降、Linux x64 + (AppImage) - **CPU**: Apple Silicon (M1/M2/M3) または Intel 64-bit、Windows 64-bit (x64) - **AIアシスタントCLI**(Claude Code、Gemini CLI、OpenAI Codex等のいずれか必須) - **2GB以上の空きディスク容量** @@ -113,6 +137,7 @@ codex --version 📱 3分で始める KawAIi Code #### 最初のAI会話 + 1. **Claude Code CLI が正常にインストールされていることを確認** ```bash claude --version @@ -122,7 +147,9 @@ codex --version 4. 右側の3Dキャラクター(モネ)がAIの返答に合わせて表情変化 #### 音声機能を有効にする(オプション) -- AivisSpeech Engine (`127.0.0.1:10101`) または VOICEVOX (`127.0.0.1:50021`) を起動 + +- AivisSpeech Engine (`127.0.0.1:10101`) または VOICEVOX (`127.0.0.1:50021`) + を起動 - 音声機能を有効にすると、キャラクターが返答を読み上げます - 感情に応じた自然な表情変化を楽しめます @@ -132,24 +159,29 @@ codex --version ⚙️ 初回セットアップ #### 音声機能の設定(オプション) + 以下のいずれかの方法で音声機能を有効にできます: **方法1: AivisSpeech Engine(ローカル)** + 1. [AivisSpeech公式サイト](https://aivis-project.com/)からダウンロード・インストール 2. AivisSpeech Engine を起動(ポート: `127.0.0.1:10101`) 3. KawAIi Code で音声機能が自動的に有効化 **方法2: VOICEVOX(ローカル)** + 1. [VOICEVOX公式サイト](https://voicevox.hiroshiba.jp/)からダウンロード・インストール 2. VOICEVOX を起動(ポート: `127.0.0.1:50021`) 3. 設定画面 → 音声エンジン選択で「VOICEVOX」を選択し、テスト再生で接続確認 **方法3: Aivis Cloud API(クラウド)** + 1. 設定画面から「クラウドAPI使用」を有効化 2. Aivis Cloud APIキーを設定 3. インターネット経由で音声合成を利用 #### 作業ディレクトリの設定 + - 設定画面から作業したいプロジェクトフォルダを選択 - Claude Codeがそのフォルダで動作します @@ -161,17 +193,21 @@ codex --version 🤖 Claude Code ガイド #### Claude Code 機能 + **🔹 通常モード** + - **用途**: 一般的なプログラミング支援、コードレビュー - **特徴**: 安全性重視、権限チェックあり - **推奨**: 通常の開発作業全般 -**🔹 Dangerous モード** +**🔹 Dangerous モード** + - **用途**: システム設定変更、高度な操作 - **特徴**: 権限チェックをスキップ、より自由度が高い - **注意**: ⚠️ 上級者向け、慎重に使用してください #### CLAUDE.md設定(手動) + - **設定画面**から`CLAUDE.md`の内容を編集可能 - **「生成」ボタン**で作業ディレクトリに`CLAUDE.md`を作成 - プロジェクト固有のAI指示を設定できます @@ -182,35 +218,43 @@ codex --version 🎭 VRMキャラクター活用法 #### デフォルトキャラクター + - **モネ**: 照れ屋で優しいプログラミングアシスタント - **表情変化**: AI応答の感情に合わせて自動変更 - **自然な動き**: 待機中も自然な動作 #### カスタムキャラクター + **VRMファイルのアップロード** + 1. 右上の **⚙️ 設定** ボタンをクリック 2. **「VRMファイルを選択」** をクリック 3. お好みの `.vrm` ファイルを選択 **対応VRM形式** + - **VRM 0.x**: フル対応 - **VRM 1.0**: 基本対応 - **ファイルサイズ**: 推奨 50MB以下 #### 表情・動作機能 + **🔹 感情パターン** -| AI応答内容 | 表情変化 | -|------------|----------| -| 成功・完了 | 😊 喜び | + +| AI応答内容 | 表情変化 | +| ------------ | ------------- | +| 成功・完了 | 😊 喜び | | エラー・失敗 | 😟 困った表情 | -| 説明・解説 | 🤔 真剣な表情 | -| 褒められた時 | 😳 照れ | +| 説明・解説 | 🤔 真剣な表情 | +| 褒められた時 | 😳 照れ | **🔹 音声連動** + - **LipSync**: 音声再生時の口の動き - **自然な表情**: 読み上げ内容に応じた表情変化 -> **🎨 カスタマイズヒント**: VRoidStudio等で作成したオリジナルキャラクターも使用可能! +> **🎨 カスタマイズヒント**: +> VRoidStudio等で作成したオリジナルキャラクターも使用可能! @@ -218,9 +262,11 @@ codex --version 🔊 音声機能設定ガイド #### 音声機能の選択肢 + KawAIi Codeでは2つの音声合成方法を選択できます: **🔹 AivisSpeech Engine(ローカル)** + 1. **AivisSpeech Engine** をダウンロード - 公式サイト: [https://aivis-project.com/](https://aivis-project.com/) - 対応OS: Windows, macOS, Linux @@ -232,6 +278,7 @@ KawAIi Codeでは2つの音声合成方法を選択できます: - AI応答時に音声読み上げ開始 **🔹 Aivis Cloud API(クラウド)** + 1. **設定画面を開く** - 右上の⚙️設定ボタンをクリック 2. **クラウドAPI設定** @@ -242,7 +289,8 @@ KawAIi Codeでは2つの音声合成方法を選択できます: - インターネット経由で高品質音声合成 - システムリソース使用量が少ない -> **💡 選択のコツ**: +> **💡 選択のコツ**: +> > - **ローカル**: 低遅延、オフライン利用可能 > - **クラウド**: 高品質、システム負荷軽減 @@ -251,46 +299,31 @@ KawAIi Codeでは2つの音声合成方法を選択できます:
🎨 カスタマイズ & 設定 -#### 壁紙システム -**🔹 カスタム壁紙** -1. 設定画面から **「壁紙アップロード」** -2. 対応形式: `JPG`, `PNG`, `MP4` (動画壁紙対応!) -3. 推奨解像度: 1920x1080以上 - -#### ターミナル設定 -**タブ機能** -- **最大10個** のターミナルを同時実行 -- タブ切り替えで複数プロジェクト管理 -- 各タブで異なるディレクトリ設定可能 -- ドラッグ&ドロップでタブ順序変更 -- ダブルクリックまたは右クリックでタブ名変更 - -**表示設定** -- **眼のアイコン**: ターミナル表示/非表示切り替え -- **フォントサイズ**: 設定画面で調整可能 -- **カラーテーマ**: ダーク/ライトモード対応 - -#### プロジェクト設定 -**作業ディレクトリ** -- プロジェクトごとに異なるディレクトリ設定 -- Claude Code起動時に自動でディレクトリ移動 -- 設定は自動保存・復元 - -**プロジェクト固有設定** -- **CLAUDE.md**: 設定画面で内容を編集し、手動で生成 -- **設定ファイル**: プロジェクトごとの個別設定保存 -- **キャラクター設定**: プロジェクト別キャラクター割り当て - -> **🚀 プロTip**: -> - 複数プロジェクトで異なるキャラクターを使い分け -> - プロジェクト固有のCLAUDE.mdでAI特化 -> - タブ機能でフロント・バック同時開発 +#### テーマ & 壁紙 + +- **VS Code Dark**: + エンジニア利用に特化したダークテーマ。ターミナル背景と一体化します。 +- **壁紙**: JPG/PNG/MP4(動画)をアップロード可能。 +- **推し色テーマ**: ピンク、ブルー、グリーン、オレンジ、パープルから選択可能。 + +#### ターミナル機能 (New!) + +- **タブ管理**: 複数のターミナルをタブで管理。ドラッグで並び替え可能。 +- **画面分割**: タブ内で `右クリック` -> `Split Right/Down` で画面分割が可能。 +- **ドラッグ移動**: ウィンドウ上部のヘッダーバーを掴んで自由に配置変更。 + +#### AIプロジェクト管理 + +- **CWD設定**: プロジェクトごとに「作業ディレクトリ」を個別に記憶。 +- **MD生成**: + CLIツールのためのコンテキストファイル(`CLAUDE.md`等)をGUIから生成。キャラクターの口調やプロジェクトのルールを定義できます。
## 🛠️ 技術スタック ### フロントエンド + - **Electron**: デスクトップアプリフレームワーク - **Next.js**: VRMビューワー(静的ファイル化済み) - **Three.js**: 3Dレンダリングエンジン @@ -298,6 +331,7 @@ KawAIi Codeでは2つの音声合成方法を選択できます: - **xterm.js**: ターミナルエミュレーター ### バックエンド & アーキテクチャ + - **Node.js**: Electronメインプロセス - **node-pty**: ターミナルプロセス管理 (Claude Code / Gemini Code Assist) - **AivisSpeech**: 音声合成エンジン連携 @@ -305,6 +339,7 @@ KawAIi Codeでは2つの音声合成方法を選択できます: - **LocalStorage**: ユーザー設定の永続化 ### 配布・ビルド + - **electron-builder**: パッケージング・配布 - **webpack**: モジュールバンドラー @@ -352,57 +387,53 @@ KawAIi-Code/ ├── package.json # Electronアプリ設定 ├── main.js # Electronメインプロセス ├── src/ -│ ├── index.html # メインHTML -│ ├── app.js # メインレンダラープロセス(1163行) -│ ├── modules/ # 独立モジュール -│ │ ├── wallpaper-system.js # 壁紙システム(372行) -│ │ └── config-manager.js # 設定管理(244行) -│ ├── preload.js # プリロードスクリプト -│ ├── voiceService.js # 音声機能サービス -│ └── vrm-viewer/ # Next.jsビルド済み静的ファイル -│ ├── index.html # VRMビューワー -│ ├── kotone_claude1.vrm # デフォルトVRMモデル -│ └── _next/ # Next.jsアセット -├── ai-kawaii-nextjs/ # Next.jsソースコード -│ ├── src/ -│ │ ├── app/ # App Router -│ │ ├── components/ # VRMViewer等 -│ │ └── features/ # LipSync等機能 -│ └── public/ # パブリックアセット -├── assets/ -│ └── icons/ # アプリアイコン -├── dist/ # 配布ファイル出力 -└── docs/ # ドキュメント +│ ├── index.html # メインHTML Layout +│ ├── app.js # アプリケーションエントリーポイント +│ ├── modules/ # 機能モジュール (Theme, Config, VRM, etc.) +│ ├── classes/ # クラス定義 (TabManager, TerminalWrapper, etc.) +│ ├── services/ # サービス層 (Audio, Terminal, Integration) +│ ├── styles/ # CSS (包括的なテーマ定義) +│ └── vrm-viewer/ # VRM表示用サブシステム +├── ai-kawaii-nextjs/ # Next.jsベースのUIコンポーネントソース +└── docs/ # ドキュメント類 ``` ### 設計思想・アーキテクチャ #### 1. ハイブリッドアーキテクチャ -- **Electronメインプロセス**: AIアシスタント統合 (Claude Code / Gemini Code Assist)、音声機能、システム統合 -- **Next.js VRMビューワー**: 3Dキャラクター表示・アニメーション(静的ファイル化) + +- **Electronメインプロセス**: AIアシスタント統合 (Claude Code / Gemini Code + Assist)、音声機能、システム統合 +- **Next.js VRMビューワー**: + 3Dキャラクター表示・アニメーション(静的ファイル化) - **PostMessage通信**: プロセス間通信でリアルタイム連携 #### 2. モジュラー設計による保守性向上 + - **壁紙システム**: 時間帯別切り替え、ユーザーアップロード、アニメーション制御 - **設定管理**: キャラクター変更、プロジェクト固有設定、ユーザー設定の一元管理 - **責務分離**: 機能ごとの独立モジュールで可読性・メンテナンス性を向上 #### 3. 動的なAIプロンプト管理 + - AIアシスタント起動時に、キャラクター設定と作業ディレクトリ内の`.md`ファイルを統合したプロンプトを生成 - Claude Code向けにはホームディレクトリに`CLAUDE.md`を生成し、停止時に削除 - Gemini Code Assist向けには作業ディレクトリに`GEMINI.md`を生成し、停止時に削除 #### 4. 静的ファイル統合による配布最適化 + - Next.js開発サーバー依存を排除 - webpack相対パス設定で単体動作実現 - file://プロトコル対応によるセキュリティ確保 #### 5. パフォーマンス最適化 + - VRMレンダリング35fps制限 - LipSyncサンプルレート最適化(CPU使用率削減) - 条件付きレンダリング・アニメーション間引き #### 6. ユーザビリティ重視 + - ワンクリックインストール(DMG配布) - 設定UI統合(VRM変更・デフォルト読み込み) - 直感的な操作フロー @@ -410,17 +441,22 @@ KawAIi-Code/ ## 🐛 トラブルシューティング ### 起動時の問題 + **セキュリティ警告が表示される場合:** + 1. システム環境設定 → セキュリティとプライバシー 2. 「このまま開く」をクリック 3. または:Control+クリックでコンテキストメニューから「開く」 **VRMキャラクターが表示されない場合:** + - アプリを完全に終了し、再起動 - macOSのセキュリティ設定でファイルアクセス許可確認 ### Claude Code関連 + **Claude Codeが起動しない場合:** + ```bash # Claude Code確認 claude --version @@ -434,18 +470,23 @@ chmod +x $(which claude) ``` **「Claude Code が見つかりません」エラーの場合:** + 1. Claude Code CLI がインストールされていることを確認 2. ターミナルで `claude --version` が実行できることを確認 3. PATHにclaude コマンドが含まれていることを確認 ### 音声機能 + **音声が再生されない場合:** + 1. AivisSpeech Engineが起動しているか確認 2. ポート10101が使用可能か確認 3. システムの音声出力設定を確認 ### パフォーマンス + **動作が重い場合:** + - VRMフレームレートは自動調整済み(35fps) - 他のアプリケーションを終了してメモリを確保 - Activity Monitorでシステムリソース確認 diff --git a/src/index.html b/src/index.html index 925072d..abc905e 100644 --- a/src/index.html +++ b/src/index.html @@ -658,14 +658,7 @@

使い方ガイド

class="help-nav-item" data-section="character" > - VRMキャラクター - - - AIアシスタント機能 + キャラクター設定 使い方ガイド >

クイックスタート

-

使い方ガイド

+

はじめに

- KawAIi Codeの詳しい使い方については、公式使い方ガイドをご覧ください。初期設定から便利な機能まで、わかりやすく解説しています。 + KawAIi Codeへようこそ!このアプリは、可愛いキャラクターと一緒に作業できるターミナルアプリです。

-

最初の会話

+

ステップ 1: ターミナルを使う

  1. - ターミナル画面でコマンド(例: - `claude`)を入力して対話開始 + アプリ起動後、ターミナルに直接コマンドを入力できます。
  2. - 右側の3Dキャラクター(モネ)がAIの返答に合わせて表情変化 + +ボタンで新しいタブを追加、右クリックで画面分割も可能です。
-

音声機能を有効にする(オプション)

-

- 音声機能を有効にすると、キャラクターが返答を読み上げます。感情に応じた自然な表情変化を楽しめます。 -

-
- - - -
-

音声機能

-
-

AivisSpeech Engine(ローカル)

-

- AivisSpeech公式サイトからAivisSpeech - Engineをダウンロード・インストールしてください。通常はアプリを起動するだけでOKです。Engine起動後(ポート: - 127.0.0.1:10101)、KawAIi - Codeで音声機能が自動的に有効化されます。特別な設定は不要で、バックグラウンドで自動連携します。 -

-
    +

    ステップ 2: キャラクターを設定

    +
    1. - 設定画面 → - 音声エンジン選択で「ローカル(AivisSpeech)」を選択 + 右上の設定ボタン(歯車アイコン)をクリック。
    2. - 話者一覧が読み込まれたら「テスト再生」で接続確認 + 「キャラクター設定」から表示キャラや読み上げキャラを選択できます。
    3. +
    4. + VRMファイルを追加して、好きなキャラクターを登録することも可能です。 +
    5. +
    +
+
+

便利な機能

+
    +
  • 音声読み上げ: AivisSpeechやVOICEVOXを起動しておくと、ターミナル出力を読み上げてくれます。
  • +
  • マルチタブ: 複数のターミナルをタブで切り替えたり、画面を分割して並行作業ができます。
  • +
  • テーマ変更: ダークテーマなど、お好みのテーマを設定画面から選択できます。
+
+ +
+

音声機能

-

VOICEVOX(ローカル)

+

AivisSpeech / VOICEVOX (ローカル)

- VOICEVOX公式サイトからVOICEVOXをダウンロード・インストールしてください。通常はアプリを起動するだけでOKです。VOICEVOX起動後(ポート: - 127.0.0.1:50021)、KawAIi - Codeで音声機能が自動的に有効化されます。特別な設定は不要で、バックグラウンドで自動連携します。 + 別途ソフトウェアをインストール・起動するだけで、自動的に接続されます。 + バックグラウンドで起動しておけば、特別な設定なしで高品質な読み上げ機能を利用できます。

    -
  • - 設定画面 → - 音声エンジン選択で「VOICEVOX」を選択 -
  • -
  • - 必要に応じてVOICEVOXエンドポイントを編集(既定: - http://127.0.0.1:50021) -
  • -
  • - 話者一覧が読み込まれたら「テスト再生」で接続確認 -
  • +
  • 設定画面 → 音声エンジン選択で「ローカル(AivisSpeech)」または「VOICEVOX」を選択
  • +
  • 話者一覧が読み込まれたら「テスト再生」で接続確認

Aivis Cloud API(クラウド)

- 設定画面から「クラウドAPI使用」を有効化し、Aivis - Cloud - APIキーを設定してください。インターネット経由で高品質な音声合成を利用できます。ローカルソフト不要でシステムリソース使用量が少ないのが利点です。 -

-

⚠️ 音声合成モデルの設定が必要

-

- 音声機能を使用するには、音声合成モデルの設定が必要です。デフォルトの音声モデルは含まれていないため、以下の手順で設定してください: -

-
    -
  1. - AivisHubからお好みの音声モデルを探します -
  2. -
  3. - モデルページでUUIDをコピーします -
  4. -
  5. - 設定画面の「音声合成モデルを追加」でUUIDを入力 -
  6. -
  7. - 「追加」ボタンをクリックして話者選択に追加 -
  8. -
  9. - 話者選択ドロップダウンから使用したいモデルを選択 -
  10. -
-

- モデルが設定されていない場合、音声合成は動作せず、エラーメッセージが表示されます。 + 設定画面から「クラウドAPI使用」を有効化し、APIキーを設定することで、ローカル負荷なしで高品質音声を利用できます。 +
+ ※音声合成モデルのUUID設定が必要です。

- +
-

VRMキャラクター

+

キャラクター設定

-

デフォルトキャラクター

+

表示モード

- モネは照れ屋で優しいプログラミングアシスタントです。AI応答の感情に合わせて表情が自動変更され、待機中も自然な動作をします。 + 設定画面の「表示設定」から、3つの表示モードを選択できます。

+
    +
  • シングルキャラ: お気に入りの1体を大きく表示します。
  • +
  • アイコンのみ: 3D描画を抑えて動作を軽くしたい時に適したモードです。
  • +
  • マルチキャラ: 登録した複数のキャラクターを並べて表示します。
  • +
+
+
+

表示キャラクター

+

+ 画面右側のキャラクターアイコンから、表示するキャラクターを切り替えられます。 + VRMファイルを追加することで、お好きなキャラクターを登録可能です。 +

+
    +
  • VRM 0.x および VRM 1.0 形式に対応
  • +
  • ドラッグ&ドロップまたはファイル選択でインポート
  • +
-

カスタムキャラクター

+

読み上げキャラクター

- 右上の設定ボタンをクリックし、「VRMファイルを選択」からお好みの.vrmファイルを選択できます。VRM - 0.x(フル対応)、VRM - 1.0(基本対応)に対応しており、ファイルサイズは50MB以下を推奨しています。 + 画面上のアイコンから、ターミナル出力を読み上げる際の声(話者)を設定できます。 + 表示キャラと読み上げキャラは別々に設定可能です。

+
    +
  • キャラクターごとに異なる話者を割り当て可能
  • +
  • 「テスト再生」ボタンで設定した声を確認できます
  • +
-

表情・動作機能

+

表情・リアクション

+

+ キャラクターはターミナルの出力内容に応じて表情が変化します。 + 音声再生時はリップシンク(口パク)も連動します。 +

- - + + @@ -891,60 +849,8 @@

表情・動作機能

- - - -
- AI応答内容 - - 表情変化 - ターミナル出力表情変化
説明・解説 真剣な表情
褒められた時照れ
-

- 音声連動機能により、LipSync(音声再生時の口の動き)と自然な表情変化が実現されています。 -

-
-
- - -
-

AIアシスタント機能

-
-

Claude Code

-

- Anthropic社のClaude Code - CLIツールを使用します。一般的なプログラミング支援、コードレビューに使用でき、安全性重視で権限チェックありのため、通常の開発作業全般に推奨されます。 -

-
-
-

Claude Code (Dangerous)

-

- Claude - Codeの上級者向けモードです。システム設定変更、高度な操作に使用します。権限チェックをスキップしてより自由度が高い操作が可能ですが、慎重に使用してください。 -

-
-
-

Gemini CLI

-

- Google社のGemini - CLIツールを使用します。プログラミング支援や質問応答など、幅広いタスクに対応できます。 -

-
-
-

OpenAI Codex

-

- OpenAI社のCodex - CLIツールを使用します。自然言語でコードを生成・編集でき、マルチモーダル入力(テキスト、スクリーンショット、図)に対応しています。 -

-
-
-

マークダウン設定(手動)

-

- 各AIアシスタントにモネの魂を吹き込むため、作業ディレクトリにMDファイルを生成する必要があります。このファイルがないと音声読み上げ機能(◆◇で囲まれた部分の読み上げ)が正常に動作しません。 -

-

- キャラクター設定は自由に変更できますが、このアプリはモネを想定して作られているため(表情連動など)、他のキャラクター設定では一部機能が正常に動作しない可能性があります。 -

-

- 設定画面の「マークダウン設定」から各ファイルの内容を編集し、「生成」ボタンで作業ディレクトリに作成できます。 -

@@ -955,22 +861,33 @@

マークダウン設定(手動)

>

カスタマイズ

-

壁紙システム

+

テーマ設定

- 設定画面から「壁紙アップロード」でカスタム壁紙を設定できます。JPG、PNGに対応しており、ファイルサイズは5MB以下、推奨解像度は1920x1080以上です。 + 設定画面の「テーマ設定」から、アプリ全体の配色を変更できます。

+
    +
  • カラーテーマ: オレンジ、ピンク、ブルー、グリーン、パープルなど、推し色に合わせて変更可能です。
  • +
  • ダークテーマ: エンジニア向けのダークテーマも用意しています。ターミナル背景と一体化した見やすい配色です。
  • +
-

ターミナル設定

+

壁紙システム

- 最大10個のターミナルを同時実行できるタブ機能があります。眼のアイコンで表示/非表示を切り替えできます。 + 設定画面から「壁紙アップロード」でカスタム壁紙を設定できます。JPG、PNGに対応しており、ファイルサイズは5MB以下、推奨解像度は1920x1080以上です。 +
+ ※「VS Code Dark」テーマ選択時は、視認性を確保するため壁紙が無効化され、ダークグレーの単色背景になります。

-

プロジェクト設定

+

ターミナル機能

- プロジェクトごとに異なる作業ディレクトリを設定できます。MDファイルは設定画面で内容を編集し手動で生成、キャラクター設定もプロジェクト別に割り当て可能です。 + 高度なターミナル管理機能を利用できます。

+
    +
  • タブ機能: 「+」ボタンで新しいターミナルタブを追加し、複数の作業を並行して行えます。
  • +
  • 画面分割: タブを右クリックして「分割」を選ぶと、1つのタブ内でターミナルを上下左右に分割できます。
  • +
  • ドラッグ移動: アプリ上部のヘッダーバーを掴んで、ウィンドウ全体を自由に移動できます。
  • +
@@ -991,37 +908,33 @@

操作ボタン

- 開始ボタン + 新規タブ (+) - AIアシスタントを起動 + 新しいターミナルタブを作成します。 - 停止ボタン + 設定 (歯車) - AIアシスタントを停止 + アプリの設定モーダルを開きます。テーマ、音声、キャラクターなどの設定が可能です。 - 表示切り替え + ヘルプ (?) - ターミナル表示/非表示 + このヘルプ画面を表示します。 - 設定ボタン - - - 音声・VRM・壁紙・ディレクトリ・MDファイル設定 + ターミナル表示 (眼) + ターミナルの表示/非表示を切り替えます。キャラとゆっくり話したい時は非表示がおすすめ。 - ヘルプボタン + キャラ変更 (アイコン) - この使い方ガイドを表示 + キャラクターを素早く切り替えるオーバーレイボタンです。 @@ -1155,15 +1068,18 @@

アップデート情報

style="background: var(--theme-bg-secondary); padding: 20px; border-radius: 8px; font-size: 14px; border: 1px solid var(--theme-primary-alpha-20)" >

- 現在のバージョン + 最新の変更点

+
    +
  • ダークテーマ: エンジニア向けのダークテーマが登場しました。
  • +
  • ターミナル機能強化: タブ分割機能、ドラッグ移動機能を追加しました。
  • +
  • キャラクター設定: 表示キャラと読み上げキャラを個別に設定可能になりました。
  • +
  • 表示モード: 3つの表示モード(シングル/アイコン/マルチ)を選択可能になりました。
  • +

- 開発版(ベータ) -

-

- 最新の機能と改善が含まれています。 + 最終更新: 2026/01/12

From 30b5d61f3d4cd160fbc284adfc4cba7388493fe5 Mon Sep 17 00:00:00 2001 From: Nao Date: Mon, 12 Jan 2026 19:30:37 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E3=83=91=E3=83=83=E3=82=B1=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E7=89=88=E3=81=A7=E3=81=AEVRM=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=A8=E3=83=91=E3=82=B9=E8=A7=A3=E6=B1=BA=E3=81=AE=E5=95=8F?= =?UTF-8?q?=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - fix-paths.js: RSCペイロード内の絶対パスを相対パスに修正 - config-manager.js: モネのVRMパスを public から out に変更 - main.js: app.asar.unpacked パスの解決と out へのフォールバックロジック追加 - package.json: out 内のVRMファイルを asarUnpack に追加 - character-display-manager.js: iframe取得のフォールバック追加 Co-authored-by: Gemini --- ai-kawaii-nextjs/fix-paths.js | 8 +++++ main.js | 39 ++++++++++++++++++++++-- package.json | 4 ++- src/modules/character-display-manager.js | 34 +++++++++++++++------ src/modules/config-manager.js | 8 ++--- 5 files changed, 76 insertions(+), 17 deletions(-) diff --git a/ai-kawaii-nextjs/fix-paths.js b/ai-kawaii-nextjs/fix-paths.js index 8358d79..fd223a4 100644 --- a/ai-kawaii-nextjs/fix-paths.js +++ b/ai-kawaii-nextjs/fix-paths.js @@ -23,6 +23,14 @@ function fixHtmlFile(filePath) { content = content.replace(/:"\/_next\//g, ':"./_next/'); content = content.replace(/,"\/_next\//g, ',"./_next/'); + // React Server Components (RSC) payload内のパスも修正 + // HL["/_next/ -> HL["./_next/ (CSS/JS プリロード) + content = content.replace(/HL\["\/_next\//g, 'HL["./_next/'); + // \\"/_next/ -> \\"./_next/ (エスケープされたJSONパス) + content = content.replace(/\\"\/_next\//g, '\\"./_next/'); + // "href":"/_next -> "href":"./_next (React要素のプロパティ) + content = content.replace(/"href":"\/_next\//g, '"href":"./_next/'); + // favicon ya svg も同様に content = content.replace(/(href|src)="\/(favicon|file\.svg|globe\.svg|next\.svg|vercel\.svg|window\.svg|settings-icon\.svg)/g, '$1="./$2'); diff --git a/main.js b/main.js index b280f2e..cc08a13 100644 --- a/main.js +++ b/main.js @@ -1060,13 +1060,48 @@ function stopHookNotificationWatcher() { ipcMain.handle('load-vrm-file', async (event, filename) => { try { let vrmPath = filename; + + // パス修正ロジックを追加(もしパスに public が含まれていて存在しない場合、 out を試す) + if (vrmPath.includes('ai-kawaii-nextjs/public/') && !fs.existsSync(vrmPath)) { + const fallbackPath = vrmPath.replace('ai-kawaii-nextjs/public/', 'ai-kawaii-nextjs/out/'); + debugLog('VRMファイルパス修正試行:', vrmPath, '->', fallbackPath); + vrmPath = fallbackPath; + } + // 相対パスの場合は __dirname と結合 - if (!path.isAbsolute(filename)) { - vrmPath = path.join(__dirname, filename); + if (!path.isAbsolute(vrmPath)) { + vrmPath = path.join(__dirname, vrmPath); + } + + // パッケージ化されたアプリでは、asarUnpackされたファイルはapp.asar.unpackedにある + // app.asar内を指している場合はapp.asar.unpackedに変換して確認 + if (vrmPath.includes('app.asar') && !vrmPath.includes('app.asar.unpacked')) { + const unpackedPath = vrmPath.replace('app.asar', 'app.asar.unpacked'); + if (fs.existsSync(unpackedPath)) { + vrmPath = unpackedPath; + debugLog('VRMファイル: asarUnpackedパスを使用:', vrmPath); + } } debugLog('VRMファイル読み込み中:', vrmPath); + if (!fs.existsSync(vrmPath)) { + // 最後の試みとして ai-kawaii-nextjs/out を直接探す + if (vrmPath.includes('ai-kawaii-nextjs')) { + const parts = vrmPath.split(path.sep); + const idx = parts.findIndex(p => p === 'ai-kawaii-nextjs'); + if (idx !== -1) { + const baseDir = parts.slice(0, idx + 1).join(path.sep); + const fileName = path.basename(vrmPath); + const lastChancePath = path.join(baseDir, 'out', fileName); + if (fs.existsSync(lastChancePath)) { + vrmPath = lastChancePath; + debugLog('VRMファイル: 最終手段で見つかったパス:', vrmPath); + } + } + } + } + if (!fs.existsSync(vrmPath)) { throw new Error(`VRM file not found: ${vrmPath}`); } diff --git a/package.json b/package.json index 956a328..c9f4a23 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,9 @@ "!ai-kawaii-nextjs/server.log" ], "asarUnpack": [ - "node_modules/node-pty/**/*" + "node_modules/node-pty/**/*", + "ai-kawaii-nextjs/out/*.vrm", + "ai-kawaii-nextjs/out/*.vrma" ], "mac": { "icon": "assets/icons/new-app-icon.icns", diff --git a/src/modules/character-display-manager.js b/src/modules/character-display-manager.js index 56ab108..d93e109 100644 --- a/src/modules/character-display-manager.js +++ b/src/modules/character-display-manager.js @@ -627,8 +627,16 @@ class CharacterDisplayManager { return; } - // iframeが読み込まれるのを待つ - const iframe = this.iframes.get(charId); + // iframeをマップから取得、見つからない場合はDOMから直接取得 + let iframe = this.iframes.get(charId); + if (!iframe) { + iframe = document.getElementById(`vrm-iframe-${charId}`); + if (iframe) { + this.iframes.set(charId, iframe); + console.log(`[CDM] iframe for ${charId} was not in map, added from DOM`); + } + } + if (iframe) { console.log(`[CDM] Waiting for viewer-ready message from ${charId}...`); @@ -656,17 +664,23 @@ class CharacterDisplayManager { } else { console.log(`[CDM] Viewer ${charId} is ready.`); } + } else { + console.error(`[CDM] iframe not found for ${charId}, cannot send VRM data`); + return; } console.log(`[CDM] Sending loadVRM message to ${charId}`); - const sent = this.postToViewerById(charId, { - type: 'loadVRM', - fileData: result.data, - fileName: result.filename || vrmPath - }); - - if (!sent) { - console.error(`[CDM] Failed to send loadVRM message to ${charId}`); + + // 直接iframeのcontentWindowを使用して送信 + if (iframe && iframe.contentWindow) { + iframe.contentWindow.postMessage({ + type: 'loadVRM', + fileData: result.data, + fileName: result.filename || vrmPath + }, '*'); + console.log(`[CDM] Successfully sent loadVRM message to ${charId}`); + } else { + console.error(`[CDM] Failed to send loadVRM message to ${charId}: iframe or contentWindow is null`); } } catch (error) { console.error('[CDM] Error in loadAndSendCharacterById:', error); diff --git a/src/modules/config-manager.js b/src/modules/config-manager.js index e7eb001..0685e68 100644 --- a/src/modules/config-manager.js +++ b/src/modules/config-manager.js @@ -126,9 +126,9 @@ class ConfigManager { if (savedCharacters && savedCharacters.length > 0) { this.characters = savedCharacters.map(c => { // モネのパスが古い形式(public/Mone_default.vrm)なら修正 - if (c.id === 'char_mona' && (c.model?.path === 'public/Mone_default.vrm' || c.vrmPath === 'public/Mone_default.vrm')) { - if (c.model) c.model.path = 'ai-kawaii-nextjs/public/Mone_default.vrm'; - c.vrmPath = 'ai-kawaii-nextjs/public/Mone_default.vrm'; + if (c.id === 'char_mona' && (c.model?.path === 'public/Mone_default.vrm' || c.vrmPath === 'public/Mone_default.vrm' || c.vrmPath?.includes('public/'))) { + if (c.model) c.model.path = 'ai-kawaii-nextjs/out/Mone_default.vrm'; + c.vrmPath = 'ai-kawaii-nextjs/out/Mone_default.vrm'; } return c; }); @@ -150,7 +150,7 @@ class ConfigManager { }, model: { type: 'vrm', - path: 'ai-kawaii-nextjs/public/Mone_default.vrm' + path: 'ai-kawaii-nextjs/out/Mone_default.vrm' }, prompt: DEFAULT_CHARACTER_PROMPT, isDefault: true From 8fcd153087d34abad8db905dbb88fc18cb36faac Mon Sep 17 00:00:00 2001 From: Nao Date: Mon, 12 Jan 2026 20:06:13 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20VRM=E8=A1=A8=E7=A4=BA=E3=81=AE?= =?UTF-8?q?=E5=90=8C=E6=9C=9F=E3=81=A8=E3=83=AD=E3=83=BC=E3=83=87=E3=82=A3?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E4=BD=93=E9=A8=93=E3=81=AE=E6=9C=80=E9=81=A9?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Three.jsの初回描画とローディング非表示を同期 - レガシーな LoadingScreen.js を削除し起動を高速化 - app.js での ReferenceError を修正 Co-authored-by: Claude Code --- ai-kawaii-nextjs/src/components/VRMViewer.tsx | 33 +++- .../src/features/vrm/hooks/useThreeScene.ts | 22 ++- src/app.js | 16 +- src/components/LoadingScreen.js | 171 ------------------ src/index.html | 1 - 5 files changed, 51 insertions(+), 192 deletions(-) delete mode 100644 src/components/LoadingScreen.js diff --git a/ai-kawaii-nextjs/src/components/VRMViewer.tsx b/ai-kawaii-nextjs/src/components/VRMViewer.tsx index 5fbcf31..de8f825 100644 --- a/ai-kawaii-nextjs/src/components/VRMViewer.tsx +++ b/ai-kawaii-nextjs/src/components/VRMViewer.tsx @@ -40,6 +40,9 @@ export default function VRMViewer({ className }: VRMViewerProps) { // アニメーション制御フック const { loadIdleAnimation } = useAnimation({ mixerRef }) + // レンダリング開始フラグ(ロード画面の隙間を無くすため) + const [isFirstFrameDrawn, setIsFirstFrameDrawn] = React.useState(false) + // カメラ制御フック const { resetCamera } = useCamera({ cameraRef, cameraControlsRef }) @@ -65,7 +68,19 @@ export default function VRMViewer({ className }: VRMViewerProps) { // デフォルトVRMを読み込む(フックから取得) const loadDefaultVRM = vrmLoader.loadDefaultVRM + // ロード開始時に描画フラグをリセット + useEffect(() => { + if (loading) { + setIsFirstFrameDrawn(false) + } + }, [loading]) + // Three.jsシーンの初期化 + const handleFirstFrameRendered = React.useCallback(() => { + console.log('✨ [VRMViewer] First frame rendered, hiding loading screen') + setIsFirstFrameDrawn(true) + }, []) + useThreeScene({ canvasRef, sceneRef, @@ -81,7 +96,8 @@ export default function VRMViewer({ className }: VRMViewerProps) { cameraControlsRef, loadVRMFile, loadDefaultVRM, - setVrmInfo + setVrmInfo, + onFirstFrameRendered: handleFirstFrameRendered }) // 自動読み込みは無効化 - CharacterDisplayManagerが全VRM読み込みを管理 @@ -135,11 +151,16 @@ export default function VRMViewer({ className }: VRMViewerProps) { }} /> - {loading && ( -
-
-
-

VRMを読み込み中...

+ {(loading || !isFirstFrameDrawn) && ( +
+
+
+

+ 読み込み中... +

)} diff --git a/ai-kawaii-nextjs/src/features/vrm/hooks/useThreeScene.ts b/ai-kawaii-nextjs/src/features/vrm/hooks/useThreeScene.ts index 9934af4..f8bbf73 100644 --- a/ai-kawaii-nextjs/src/features/vrm/hooks/useThreeScene.ts +++ b/ai-kawaii-nextjs/src/features/vrm/hooks/useThreeScene.ts @@ -1,4 +1,4 @@ -import { useEffect } from 'react' +import { useEffect, useRef } from 'react' import * as THREE from 'three' import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls' import { VRM } from '@pixiv/three-vrm' @@ -25,6 +25,7 @@ interface UseThreeSceneProps { loadVRMFile: (file: File) => Promise loadDefaultVRM: () => Promise setVrmInfo: (info: string) => void + onFirstFrameRendered?: () => void } export const useThreeScene = ({ @@ -42,8 +43,11 @@ export const useThreeScene = ({ cameraControlsRef, loadVRMFile, loadDefaultVRM, - setVrmInfo + setVrmInfo, + onFirstFrameRendered }: UseThreeSceneProps) => { + const isVrmFirstFrameRenderedRef = useRef(false) + useEffect(() => { if (!canvasRef.current) return @@ -157,6 +161,18 @@ export const useThreeScene = ({ } renderer.render(scene, camera) + + // VRMが読み込まれてからの初回描画完了を通知 + if (onFirstFrameRendered && vrmRef.current && !isVrmFirstFrameRenderedRef.current) { + debugLog('✨ [useThreeScene] First frame rendered with VRM') + isVrmFirstFrameRenderedRef.current = true + onFirstFrameRendered() + } + + // VRMが存在しない場合はフラグをリセット(次のロードに備える) + if (!vrmRef.current && isVrmFirstFrameRenderedRef.current) { + isVrmFirstFrameRenderedRef.current = false + } } animate(0) @@ -337,5 +353,5 @@ export const useThreeScene = ({ rendererRef.current.dispose() } } - }, [cameraRef, cameraControlsRef, emoteControllerRef, lipSyncRef, mixerRef, rendererRef, sceneRef, vrmRef, loadVRMFile, loadDefaultVRM, setVrmInfo, canvasRef, clockRef, animationIdRef, audioContextRef]) + }, [cameraRef, cameraControlsRef, emoteControllerRef, lipSyncRef, mixerRef, rendererRef, sceneRef, vrmRef, loadVRMFile, loadDefaultVRM, setVrmInfo, canvasRef, clockRef, animationIdRef, audioContextRef, onFirstFrameRendered]) } \ No newline at end of file diff --git a/src/app.js b/src/app.js index e836891..2084979 100644 --- a/src/app.js +++ b/src/app.js @@ -703,10 +703,9 @@ class TerminalApp { // Initialize the application when DOM is loaded document.addEventListener('DOMContentLoaded', () => { - // ローディング画面を即座に表示 - const loadingScreen = new LoadingScreen(); - loadingScreen.show(); + // Legacy LoadingScreen is removed to improve startup speed and avoid UI conflicts + // アプリ初期化処理 setTimeout(async () => { try { @@ -776,14 +775,9 @@ document.addEventListener('DOMContentLoaded', () => { // エラーが発生しても強制的に接続状態をチェック forcedConnectionCheck(); } - - // 初期化完了後にローディング画面を非表示 - setTimeout(() => { - loadingScreen.hide(); - // メインアプリを表示 - document.body.classList.add('loaded'); - }, 4000); - }, 1000); // 1秒間ローディング画面を表示 + // 初期化完了。即座にメインアプリを表示 + document.body.classList.add('loaded'); + }, 100); // 初回強制接続チェック(フォールバック) setTimeout(() => { diff --git a/src/components/LoadingScreen.js b/src/components/LoadingScreen.js deleted file mode 100644 index 78608f7..0000000 --- a/src/components/LoadingScreen.js +++ /dev/null @@ -1,171 +0,0 @@ -/** - * アプリ起動時のローディング画面(シンプル版) - */ -class LoadingScreen { - constructor() { - this.element = null; - this.isVisible = false; - - // 定数 - this.BACKGROUND_COLOR = '#ffffff'; - this.Z_INDEX = 9999; - this.TRANSITION_DURATION = 300; // ms - this.FADE_IN_DELAY = 50; // ms - this.STYLE_ID = 'loading-screen-styles'; - this.ELEMENT_ID = 'loading-screen'; - } - - /** - * ローディング画面を作成 - */ - create() { - if (this.element) return; - - this.element = document.createElement('div'); - this.element.id = this.ELEMENT_ID; - this.element.innerHTML = ` - - `; - - // スタイルを追加 - this.addStyles(); - - document.body.appendChild(this.element); - } - - /** - * スタイルを追加 - */ - addStyles() { - if (document.getElementById(this.STYLE_ID)) return; - - const style = document.createElement('style'); - style.id = this.STYLE_ID; - style.textContent = ` - html, body { - background: ${this.BACKGROUND_COLOR} !important; - } - - #${this.ELEMENT_ID} { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - width: 100vw; - height: 100vh; - margin: 0; - padding: 0; - z-index: ${this.Z_INDEX}; - display: flex; - align-items: center; - justify-content: center; - background: ${this.BACKGROUND_COLOR}; - opacity: 0; - visibility: hidden; - transition: opacity ${this.TRANSITION_DURATION / 1000}s ease, visibility ${this.TRANSITION_DURATION / 1000}s ease; - } - - #${this.ELEMENT_ID}.visible { - opacity: 1; - visibility: visible; - } - - .loading-logo { - max-width: 50%; - max-height: 50%; - width: auto; - height: auto; - object-fit: contain; - animation: logoFade 5s ease-in-out forwards; - } - - @keyframes logoFade { - 0% { - opacity: 0; - } - 10% { - opacity: 0; - } - 30% { - opacity: 1; - } - 70% { - opacity: 1; - } - 90% { - opacity: 0; - } - 100% { - opacity: 0; - } - } - `; - - document.head.appendChild(style); - } - - /** - * スタイルを削除 - */ - removeStyles() { - const styleElement = document.getElementById(this.STYLE_ID); - if (styleElement) { - styleElement.remove(); - } - } - - /** - * ローディング画面を表示 - */ - show() { - if (this.isVisible) return; - - this.create(); - this.isVisible = true; - - // 少し遅延してからフェードイン - setTimeout(() => { - if (this.element) { - this.element.classList.add('visible'); - } - }, this.FADE_IN_DELAY); - } - - /** - * ローディング画面を非表示 - */ - hide() { - if (!this.isVisible || !this.element) return; - - this.isVisible = false; - - // フェードアウト開始前にスタイルを削除(背景を先に戻す) - this.removeStyles(); - - this.element.classList.remove('visible'); - - // フェードアウト完了後に要素を削除 - setTimeout(() => { - if (this.element && this.element.parentNode) { - this.element.parentNode.removeChild(this.element); - this.element = null; - } - }, this.TRANSITION_DURATION); - } - - /** - * クリーンアップ - */ - destroy() { - this.hide(); - this.removeStyles(); - } -} - -// グローバルで使用できるようにエクスポート -if (typeof module !== 'undefined' && module.exports) { - module.exports = LoadingScreen; -} else { - window.LoadingScreen = LoadingScreen; -} diff --git a/src/index.html b/src/index.html index abc905e..a2925fb 100644 --- a/src/index.html +++ b/src/index.html @@ -13,7 +13,6 @@ -