Skip to content

Develop => main#563

Merged
tegnike merged 3 commits into
mainfrom
develop
Jul 5, 2026
Merged

Develop => main#563
tegnike merged 3 commits into
mainfrom
develop

Conversation

@tegnike

@tegnike tegnike commented Jul 5, 2026

Copy link
Copy Markdown
Owner

新機能

  • なし

改善

  • 設定画面の背景オーバーレイを調整し、モーダル外側クリックで閉じられるようにしました。
  • VRM、Live2D、PNGTuberのモデル読み込み中表示を共通オーバーレイに統一しました。
  • VOICEVOX/AivisSpeechの話者リスト更新でPOSTリクエストを使い、サーバー側リソース利用が許可されていない場合の案内を分かりやすくしました。

バグ修正

  • VRMモデルの連続読み込み時に古い読み込み結果が後から反映されないようにしました。
  • VOICEVOX/AivisSpeechの話者リスト更新で、サーバー応答エラーや不正なレスポンス形式を検出して失敗扱いにするようにしました。

テスト

  • 音声設定画面の話者リスト更新操作テストを追加しました。
  • VOICEVOX/AivisSpeech話者リスト更新APIのエラー応答・不正レスポンス形式テストを追加しました。

ドキュメント・翻訳

  • なし

その他

  • なし

Summary by CodeRabbit

  • New Features

    • モデル読み込み中に、各ビューアーでローディング表示が出るようになりました。
    • 設定画面の背景をクリックすると閉じられるようになりました。
  • Bug Fixes

    • モデル読み込みの競合時に、誤って読み込み完了表示へ切り替わる問題を改善しました。
    • 話者リスト更新で、サーバー応答が不正な場合のエラー表示を改善しました。
    • Aivis Speech の権限不足時に、案内メッセージをより分かりやすく表示するようになりました。

設定画面の半透明バックドロップと外側クリックでのクローズを追加。モデル読み込み中の共通スピナー表示を追加し、AivisSpeech/VOICEVOXの話者リスト更新API呼び出しとレスポンス検証も修正。
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 16 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 579a8792-3fcd-4061-affe-eb585eae6387

📥 Commits

Reviewing files that changed from the base of the PR and between bbe1098 and 32ecef5.

📒 Files selected for processing (6)
  • src/__tests__/components/voice.test.tsx
  • src/components/Live2DComponent.tsx
  • src/components/settings/index.tsx
  • src/lib/api-services/validateSpeakersResponse.ts
  • src/pages/api/update-aivis-speakers.ts
  • src/pages/api/update-voicevox-speakers.ts

Walkthrough

Live2D・VRM・PNGTuberビューアにModelLoadingOverlayコンポーネントを導入してローディング表示を統一し、VRM/Live2Dのモデル読み込みで競合するリクエストのローディング状態誤解除を防止した。AivisSpeech/VOICEVOX話者更新APIにレスポンス検証を追加し、設定画面のエラーメッセージを改善。設定バックドロップのクリッククローズとテーマCSSも調整した。

Changes

モデルローディング表示

Layer / File(s) Summary
ModelLoadingOverlayコンポーネント
src/components/modelLoadingOverlay.tsx
読み込み中を示すアクセシビリティ属性・スピナー付きオーバーレイを新規追加。
Live2Dローディング制御
src/components/Live2DComponent.tsx, src/components/live2DViewer.tsx
requestIdによる多重ロード競合制御とisModelLoading状態を追加し、各表示分岐でオーバーレイを表示。
PNGTuberローディング置換
src/components/PNGTuberComponent.tsx, src/components/pngTuberViewer.tsx
isLoading判定を簡潔化し、従来のdiv表示やnullをModelLoadingOverlayに置換。
VRMロード競合制御と通知
src/features/vrmViewer/viewer.ts, src/components/vrmViewer.tsx
loadVrmにrequestIdベースの競合制御とonModelLoadingChangeコールバックを追加し、VrmViewerがオーバーレイ表示を購読。

話者リスト更新APIとエラーメッセージ

Layer / File(s) Summary
APIレスポンス検証
src/pages/api/update-aivis-speakers.ts, src/pages/api/update-voicevox-speakers.ts, src/__tests__/pages/api/update-aivis-speakers.test.ts, src/__tests__/pages/api/update-voicevox-speakers.test.ts
fetchレスポンスのok/配列形状を検証し、不正時は例外を投げて500を返す。対応テストを追加。
エラーメッセージ改善
src/components/settings/voice.tsx, src/__tests__/components/voice.test.tsx
getSpeakerUpdateErrorMessageでServerSecretAccessDeniedを日本語メッセージ化し、VOICEVOX/AIVIS_SPEECH双方に適用。対応テストを追加。

設定バックドロップとテーマスタイル

Layer / File(s) Summary
バックドロップクリック処理とCSS
src/components/settings/index.tsx, src/styles/globals.css
バックドロップ自身のクリックのみでクローズする処理を追加し、対応する半透明背景色変数とブラーフィルタをCSSに追加。

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant VrmViewer
  participant Viewer
  participant ModelLoadingOverlay

  VrmViewer->>Viewer: loadVrm(url)
  Viewer->>Viewer: requestId採番
  Viewer->>VrmViewer: onModelLoadingChange(true)
  VrmViewer->>ModelLoadingOverlay: 表示
  Viewer->>Viewer: モデル読込完了
  alt requestIdが最新
    Viewer->>VrmViewer: onModelLoadingChange(false)
    VrmViewer->>ModelLoadingOverlay: 非表示
  else 古いrequestId
    Viewer->>Viewer: unLoadVrmのみ実行
  end
Loading
sequenceDiagram
  participant User
  participant SettingsVoice
  participant UpdateAivisSpeakersAPI
  participant AivisServer

  User->>SettingsVoice: UpdateSpeakerListクリック
  SettingsVoice->>UpdateAivisSpeakersAPI: POST /api/update-aivis-speakers
  UpdateAivisSpeakersAPI->>AivisServer: fetch /speakers
  AivisServer-->>UpdateAivisSpeakersAPI: レスポンス
  alt レスポンス不正またはok:false
    UpdateAivisSpeakersAPI-->>SettingsVoice: 500 + errorCode
    SettingsVoice->>SettingsVoice: getSpeakerUpdateErrorMessageで表示
  else 正常
    UpdateAivisSpeakersAPI-->>SettingsVoice: 200
  end
Loading

Possibly related PRs

  • tegnike/aituber-kit#228: src/components/settings/voice.tsxの話者更新処理とsrc/pages/api/update-aivis-speakers.tsのレスポンス検証を同様に拡張している。
  • tegnike/aituber-kit#508: src/features/vrmViewer/viewer.tsのVRM読み込みフロー(ロード状態制御・try/finally後処理)に同じ箇所で関与している。
  • tegnike/aituber-kit#562: ModelLoadingOverlayによるLive2D/VRM/PNGTuberローディング表示やSettingsバックドロップクリック処理を同様に実装している。
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive タイトルが「Develop => main」では変更内容を示さず、PRの主な改善点を把握できません。 設定画面の外側クリックでのクローズ、モデル読み込み用共通オーバーレイ、VOICEVOX/AivisSpeech更新処理の改善などを要約した具体的なタイトルにしてください。
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/Live2DComponent.tsx (1)

132-171: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

loadLive2DModel の競合制御が不完全(古いリクエストが新しいモデルを上書きする)

requestIdfinally 内の setIsModelLoading(false) にのみ使われており、モデル適用処理(stage.addChild / modelRef.current / setModel / homeStore.setState)は requestId のチェックなしに実行されます。selectedLive2DPath が短時間に連続変更され、古いリクエストが新しいリクエストより後に解決した場合、古いモデルがステージに追加され「現在のモデル」として上書きされてしまいます(表示上のモデル重複や、ドラッグ・スケール操作が意図しないモデルに適用される等の不具合につながります)。

viewer.ts(VRM側)は this.model !== model のチェックで同様の競合を正しく防止しており、本ファイルにも同じ考え方の適用が必要です。

🔒 提案する修正
       await new Promise((resolve, reject) => {
         newModel.once('load', () => resolve(true))
         newModel.once('error', (e) => reject(e))
         setTimeout(() => reject(new Error('Model load timeout')), 10000)
       })
 
+      // 新しいリクエストが既に開始されている場合、この結果は破棄する
+      if (requestId !== loadRequestIdRef.current) {
+        newModel.destroy()
+        return
+      }
+
       currentApp.stage.addChild(newModel as unknown as DisplayObject)
       newModel.anchor.set(0.5, 0.5)
       setModelPosition(currentApp, newModel)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/Live2DComponent.tsx` around lines 132 - 171, The request
cancellation/race handling in loadLive2DModel is incomplete because requestId is
only used in the final loading flag reset, so older loads can still overwrite
newer ones. Add a requestId/current request check in loadLive2DModel before
applying the loaded model (especially around currentApp.stage.addChild,
modelRef.current assignment, setModel, and homeStore.setState) and bail out if
the request is stale. Keep the existing finally-based loading-state guard, and
follow the same stale-model protection pattern used in viewer.ts.
🧹 Nitpick comments (3)
src/pages/api/update-aivis-speakers.ts (1)

62-73: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

レスポンス検証ロジックの重複。

response.ok チェックと Array.isArray 検証のロジックが update-voicevox-speakers.ts の同等コードとほぼ同一です。共通ヘルパー(例: validateSpeakersResponse(response, serverName))に切り出すことで重複を削減できます。

♻️ 共通化の一例
// src/lib/api-services/validateSpeakersResponse.ts
export async function fetchAndValidateSpeakers(
  response: Response,
  serverName: string
): Promise<Speaker[]> {
  if (!response.ok) {
    throw new Error(`${serverName} server responded with status: ${response.status}`)
  }
  const speakers: unknown = await response.json()
  if (!Array.isArray(speakers)) {
    throw new Error(`${serverName} speakers response must be an array`)
  }
  return speakers as Speaker[]
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pages/api/update-aivis-speakers.ts` around lines 62 - 73, The speaker
response validation in update-aivis-speakers.ts duplicates the same response.ok
and Array.isArray checks used in the VoiceVox flow, so extract that logic into a
shared helper such as validateSpeakersResponse or fetchAndValidateSpeakers under
the same API services layer. Update the updateAivisSpeakers path to call the
shared helper with the AivisSpeech server name, and reuse it from the matching
update-voicevox-speakers.ts code so both handlers share one validation
implementation.
src/__tests__/components/voice.test.tsx (1)

183-247: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

VOICEVOX 側の更新テストも追加してください。 src/__tests__/components/voice.test.tsxdescribe('voicevox settings') に、/api/update-voicevox-speakers の POST 呼び出しと ServerSecretAccessDenied のエラー表示を AIVIS と同様に追加したいです。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/__tests__/components/voice.test.tsx` around lines 183 - 247, The AIVIS
speech settings tests already cover the POST update flow and
`ServerSecretAccessDenied` handling, but the equivalent VoiceVox coverage is
missing. Add matching cases under `describe('voicevox settings')` in
`voice.test.tsx` using the existing `Voice` component and `UpdateSpeakerList`
flow: assert that the update action posts to `/api/update-voicevox-speakers`,
and add a failure test that stubs the response with `ServerSecretAccessDenied`
and verifies the same server-resource access error message is rendered.
src/components/settings/index.tsx (1)

58-61: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

キーボードでの閉じる操作も検討を。

バックドロップクリックでの閉じる処理は正しく実装されていますが、Escapeキーでの閉じる操作は追加されていません。他の場所(Headerの閉じるボタン等)で対応済みであれば問題ありませんが、モーダルとしてのアクセシビリティ観点では検討の余地があります。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/settings/index.tsx` around lines 58 - 61, The theme settings
backdrop currently closes only on click, so add keyboard-dismiss support for the
modal by handling Escape in the settings component. Update the relevant modal
logic in the settings index component, using the existing close path behind
handleBackdropClick or the header close action, and wire a keydown listener so
pressing Escape triggers the same close behavior while the modal is open.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/components/Live2DComponent.tsx`:
- Around line 132-171: The request cancellation/race handling in loadLive2DModel
is incomplete because requestId is only used in the final loading flag reset, so
older loads can still overwrite newer ones. Add a requestId/current request
check in loadLive2DModel before applying the loaded model (especially around
currentApp.stage.addChild, modelRef.current assignment, setModel, and
homeStore.setState) and bail out if the request is stale. Keep the existing
finally-based loading-state guard, and follow the same stale-model protection
pattern used in viewer.ts.

---

Nitpick comments:
In `@src/__tests__/components/voice.test.tsx`:
- Around line 183-247: The AIVIS speech settings tests already cover the POST
update flow and `ServerSecretAccessDenied` handling, but the equivalent VoiceVox
coverage is missing. Add matching cases under `describe('voicevox settings')` in
`voice.test.tsx` using the existing `Voice` component and `UpdateSpeakerList`
flow: assert that the update action posts to `/api/update-voicevox-speakers`,
and add a failure test that stubs the response with `ServerSecretAccessDenied`
and verifies the same server-resource access error message is rendered.

In `@src/components/settings/index.tsx`:
- Around line 58-61: The theme settings backdrop currently closes only on click,
so add keyboard-dismiss support for the modal by handling Escape in the settings
component. Update the relevant modal logic in the settings index component,
using the existing close path behind handleBackdropClick or the header close
action, and wire a keydown listener so pressing Escape triggers the same close
behavior while the modal is open.

In `@src/pages/api/update-aivis-speakers.ts`:
- Around line 62-73: The speaker response validation in update-aivis-speakers.ts
duplicates the same response.ok and Array.isArray checks used in the VoiceVox
flow, so extract that logic into a shared helper such as
validateSpeakersResponse or fetchAndValidateSpeakers under the same API services
layer. Update the updateAivisSpeakers path to call the shared helper with the
AivisSpeech server name, and reuse it from the matching
update-voicevox-speakers.ts code so both handlers share one validation
implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e2a84abc-2e06-41d6-bc0e-771f6c0d2c11

📥 Commits

Reviewing files that changed from the base of the PR and between 5d11a2e and bbe1098.

📒 Files selected for processing (15)
  • src/__tests__/components/voice.test.tsx
  • src/__tests__/pages/api/update-aivis-speakers.test.ts
  • src/__tests__/pages/api/update-voicevox-speakers.test.ts
  • src/components/Live2DComponent.tsx
  • src/components/PNGTuberComponent.tsx
  • src/components/live2DViewer.tsx
  • src/components/modelLoadingOverlay.tsx
  • src/components/pngTuberViewer.tsx
  • src/components/settings/index.tsx
  • src/components/settings/voice.tsx
  • src/components/vrmViewer.tsx
  • src/features/vrmViewer/viewer.ts
  • src/pages/api/update-aivis-speakers.ts
  • src/pages/api/update-voicevox-speakers.ts
  • src/styles/globals.css

@tegnike tegnike merged commit 91ebcd8 into main Jul 5, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant