Feature/game commentary mode#514
Conversation
画面キャプチャを一定間隔で自動取得し、マルチモーダルAIに送信して リアルタイム実況コメントを生成・発話する新機能を実装。 - CaptureServiceシングルトンによる画面キャプチャAPI - 専用ring bufferによる実況履歴管理(chatLog汚染防止) - 完了ベースのsetTimeoutループで発話重複を回避 - アイドルモード等との排他制御ルール追加 - 設定画面タブ・環境変数・i18n対応 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
実況コメントをchatLogに保存し、YouTube/Mastraと文脈を自動共有する。 - デフォルトでchatLogに保存するよう変更 - [実況]prefixで実況コメントを区別可能に - 実況生成時にchatLogの直近メッセージも参照して視聴者コメントの文脈を把握 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
YouTubeモードとゲーム実況モードの同時利用時にキャプチャボタンが表示されない問題を修正。 元の制限は技術的制約ではなく、全モードで常にボタンを表示するように変更。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
YouTubeモードと同じ操作体系に統一。設定ONでボタン表示、ボタン押下で実況開始/停止。 開始時にキャプチャが未開始なら自動で画面共有を開始する。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ハードコードのSUFFIXを廃止し、実況プロンプトをpublic/presets/game-commentary-prompt.txtで管理 - usePresetLoaderに登録し他のプロンプトと同じ仕組みに統一 - キャラクタープロンプトの感情タグ指示と重複しないようプロンプト内容を整理 - gameCommentarySaveToChatを実行時にgetState()で取得しクロージャの古い値参照を修正 - ボタンOFF時にSpeakQueue.stopAll()を呼び進行中の発話を停止 - isRunningRefで最新状態を参照し停止後のタイマー再開を防止 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GameCommentarySettingsはSettingsStateに含まれているため、 Record<string, unknown>へのキャストは不要。Vercelビルドエラーを修正。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Message型は[text, image]の順序で定義されているが、 [image, text]の順になっていたため型エラーが発生していた。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
直列のPromiseチェーンを廃止し、複数文のTTS合成を並列実行するよう変更。 順序管理にpendingResults Mapを導入し、合成完了順に関わらず正しい順序で SpeakQueueへエンキューする。レート制限を1秒→250msに短縮。 セッション変更時のonCompleteコールバック漏れも修正。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- AIが実況セリフとは別に画面の情景描写を生成し、裏でリングバッファに保存 - 次回キャプチャ時に過去の情景描写をコンテキストとして渡し、画面変化の比較や ゲーム進行の流れの把握を可能にする - [scene]タグによる出力フォーマットの解析を追加(後方互換性あり) - キャプチャ間隔を10-60秒から0-20秒に変更(デフォルト5秒) - 0秒設定は発話完了後に即座に次のキャプチャを開始(完了ベースsetTimeoutのため安全) - captureIntervalRefを導入し、実況中の設定変更が即座に反映されるよう修正 - ドキュメント3言語(ja/en/zh)に情景描写の仕様とキャプチャ間隔の変更を反映 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 実況テキストが分割されず一括でTTSに渡されていた問題を修正 - splitSentenceで句点・感嘆符・改行ごとに分割し、文単位で発話キューに投入 - 最後の文の完了時のみ次回キャプチャをスケジュールする動作を維持 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- 実況履歴の参照数(contextCount)の最小値を1→0に変更し、履歴参照を無効化可能に - contextCount取得時の || を ?? に修正(0がfalsyで5に置換される問題) - contextCount=0時はリングバッファへの追加をスキップ - 実況プロンプトの説明に[scene]タグの解説を追記 - 実況プロンプトのテキストエリアを縦方向にリサイズ可能に変更 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- serviceReasoningDefaultsにollama用のeffort設定を追加(none/low/medium/high) - buildReasoningProviderOptionsにollamaのcase分岐を追加 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
マルチモーダル機能の利用モード(always/never/ai-decide)を廃止し、 enableMultiModal(boolean)のみで制御するように簡素化。 既存のmultiModalMode設定はマイグレーションでenableMultiModalに変換される。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
VRMモーションタグ(think, cheer, wave, shy等10種)の説明を追加し、 既存・新規の会話例にモーションタグを組み込んだ。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Claude finished @tegnike's task —— View job PR Review: Game Commentary Mode FeatureThis PR introduces a comprehensive AI-powered game commentary system that captures screen content and generates real-time commentary. Here's my detailed review: 📋 Review Summary✅ StrengthsArchitecture & Design:
Implementation Quality:
|
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Walkthroughゲーム実況機能を新規追加し、 Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant GameCommentaryManager
participant useGameCommentaryMode
participant CaptureService
participant generateGameCommentary
participant SpeakQueue
participant HomeStore
User->>GameCommentaryManager: (enable game commentary)
activate GameCommentaryManager
GameCommentaryManager->>useGameCommentaryMode: get state & callbacks
activate useGameCommentaryMode
loop capture cycle
useGameCommentaryMode->>CaptureService: captureFrame()
activate CaptureService
CaptureService-->>useGameCommentaryMode: imageData
deactivate CaptureService
useGameCommentaryMode->>generateGameCommentary: generate commentary with image
activate generateGameCommentary
generateGameCommentary->>generateGameCommentary: build AI messages + prompt
generateGameCommentary-->>useGameCommentaryMode: {text, emotion, sceneDescription}
deactivate generateGameCommentary
useGameCommentaryMode->>SpeakQueue: enqueue sentences
activate SpeakQueue
SpeakQueue->>HomeStore: set isSpeaking = true
SpeakQueue-->>useGameCommentaryMode: (speaking)
deactivate SpeakQueue
Note over useGameCommentaryMode: transition to "speaking" state
useGameCommentaryMode->>useGameCommentaryMode: wait for speech completion
Note over useGameCommentaryMode: after speech ends, transition to "waiting"
useGameCommentaryMode->>useGameCommentaryMode: schedule next capture
end
User->>GameCommentaryManager: (user chat arrives)
GameCommentaryManager->>useGameCommentaryMode: interrupt signal
useGameCommentaryMode->>SpeakQueue: stopSession()
useGameCommentaryMode-->>GameCommentaryManager: state = "waiting"
deactivate useGameCommentaryMode
deactivate GameCommentaryManager
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
Claude finished @tegnike's task —— View job PR Review: Game Commentary Mode FeatureTodo List:
📋 Review SummaryThis PR introduces a comprehensive AI-powered game commentary system that captures screen content and generates real-time commentary. The implementation demonstrates solid architectural patterns and thorough consideration of edge cases. ✅ StrengthsExcellent Architecture & Design
Comprehensive Error Handling
Strong Test Coverage
|
There was a problem hiding this comment.
Actionable comments posted: 1
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
.agents/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx (1)
1-174:⚠️ Potential issue | 🟡 Minor整形ルール違反が多く、差分ノイズが大きいです。
Line [1] 〜 Line [174] は Prettier 指摘が大量に出ているので、コミット前に自動整形を一度通してください。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx around lines 1 - 174, Many lines in this file fail Prettier formatting; run the auto-formatter before committing to remove noise. Fix by running your project's Prettier (or `npm run format`/`npx prettier --write`) on this file and re-commit so components like MyAnimation, Title, YAxis, XAxis, and Bar are consistently formatted and the diff only contains semantic changes.src/__tests__/hooks/usePresetLoader.test.ts (1)
193-227:⚠️ Potential issue | 🟡 Minor
gameCommentaryPromptTemplateの検証が抜けています。Line 193 以降は
game-commentary-prompt.txtを読むことまでは見ていますが、モック値も state assertion も background-analysis 側しかありません。usePresetLoaderがgameCommentaryPromptTemplateを保存しなくても、このケースは通ってしまいます。💡 追加すると安全な最小差分
const presets: Record<string, string> = { 'idle-ai-prompt-template.txt': 'Idle AI template', 'youtube-prompt-evaluate.txt': 'Evaluate prompt', 'youtube-prompt-continuation.txt': 'Continuation prompt', 'youtube-prompt-sleep.txt': 'Sleep prompt', 'youtube-prompt-new-topic.txt': 'New topic prompt', 'youtube-prompt-select-comment.txt': 'Select comment prompt', 'multimodal-ai-decision-prompt.txt': 'Multimodal decision prompt', + 'game-commentary-prompt.txt': 'Game commentary prompt', 'game-commentary-background-analysis-prompt.txt': 'Background analysis prompt', } @@ + expect(state.gameCommentaryPromptTemplate).toBe( + 'Game commentary prompt' + ) expect(state.gameCommentaryBackgroundAnalysisPromptTemplate).toBe( 'Background analysis prompt' )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/__tests__/hooks/usePresetLoader.test.ts` around lines 193 - 227, The test is missing an assertion for gameCommentaryPromptTemplate: update the test that renders usePresetLoader to ensure mockLoadPreset is called with 'game-commentary-prompt.txt' (it should already be in PROMPT_PRESET_FILES) and add an expectation on settingsStore.getState().gameCommentaryPromptTemplate to equal the mocked value for that file (e.g., 'Game commentary prompt' or whatever mock mapping you use); locate the assertions block that checks other state fields (idleAiPromptTemplate, conversationContinuityPrompt*, multiModalAiDecisionPrompt, gameCommentaryBackgroundAnalysisPromptTemplate) and add the single line assertion for gameCommentaryPromptTemplate and ensure the mockLoadPreset mapping returned in the test includes the corresponding filename key so the loader actually sets the state.src/features/chat/openAIAudioChat.ts (1)
27-44:⚠️ Potential issue | 🔴 CriticalOpenAI SDK の型エラーを修正する必要があります。
as constによりmodalities: ['text', 'audio']が readonly tuple になり、OpenAI SDK の streaming overload に合致していません。ChatCompletionCreateParamsStreaming型を明示的に指定し、modalitiesを可変配列として渡してください。修正方法
-import { ChatCompletionMessageParam } from 'openai/resources/chat/completions' +import { + ChatCompletionCreateParamsStreaming, + ChatCompletionMessageParam, +} from 'openai/resources/chat/completions' - const request = { + const request: ChatCompletionCreateParamsStreaming = { model: (ss.selectAIModel as AudioModeModel) || defaultModels.openaiAudio, messages: messageSelectors.getAudioMessages( messages ) as ChatCompletionMessageParam[], stream: true, modalities: ['text', 'audio'], audio: { voice: ss.audioModeVoice as RealtimeAPIModeVoice, format: 'pcm16', }, - } as const + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/chat/openAIAudioChat.ts` around lines 27 - 44, The request object currently uses `as const` which makes `modalities: ['text','audio']` a readonly tuple that doesn't match the OpenAI streaming overload; remove the `as const`, explicitly type the request as the streaming param type (e.g. `ChatCompletionCreateParamsStreaming`) and ensure `modalities` is a normal mutable array (['text','audio']) before calling `openai.chat.completions.create` (refer to `request`, `ss.selectAIModel`, `defaultModels.openaiAudio`, `messageSelectors.getAudioMessages`, `ChatCompletionMessageParam`, `options.signal`, and `openai.chat.completions.create` to locate the code).
🟠 Major comments (28)
.agents/skills/remotion-best-practices/rules/extract-frames.md-89-92 (1)
89-92:⚠️ Potential issue | 🟠 Major中断時に成功終了してしまうため、キャンセル判定が不正確です
Line 90-92 は
breakで正常 return になるため、呼び出し側が「完了」と誤認します。中断はthrowに統一してください。修正案(abort時は必ず失敗として返す)
- for await (using videoSample of sink.samplesAtTimestamps(timestamps)) { - if (signal?.aborted) { - break; - } + for await (using videoSample of sink.samplesAtTimestamps(timestamps)) { + if (signal?.aborted) { + throw new Error("Aborted"); + }Also applies to: 170-188, 207-228
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/extract-frames.md around lines 89 - 92, The current for-await loops over sink.samplesAtTimestamps(timestamps) use "if (signal?.aborted) { break; }", which causes the function to return normally on cancellation; replace the break with throwing a cancellation error so callers see a failure. Specifically, in the loops iterating "for await (using videoSample of sink.samplesAtTimestamps(timestamps))" (and the other occurrences noted), change the abort check to "if (signal?.aborted) throw new Error('Operation aborted')" (or throw a DOMException/AbortError if you prefer) so cancellation is propagated as an exception rather than a normal return. Ensure all similar abort checks in the file (the ranges referenced) are updated consistently..agents/skills/remotion-best-practices/rules/extract-frames.md-14-16 (1)
14-16:⚠️ Potential issue | 🟠 Major
using構文は環境要件が厳しく、「any project」への貼り付けは現実的ではありませんLine 14 で「any project」への貼り付けを示唆していますが、
using構文には以下の要件があります:
- TypeScript 5.2 以上(必須)、tsconfig で
"target": "es2022"以上、"lib"に"esnext.disposable"を含める必要があります- Safari は 2026年時点で非対応です。実運用ではトランスパイルまたはポリフィルが必須になります
- Node.js 26+ でのみネイティブサポートが確実です
要件を明記するか、従来の
try/finallyによるフォールバック実装も併示してください。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/extract-frames.md around lines 14 - 16, 更新する文は、「This function can be copy-pasted into any project」という主張が誤解を招くため、`using` 構文の環境要件(TypeScript 5.2+、tsconfig `"target": "es2022"` 以上、`"lib"` に `"esnext.disposable"`、Node.js 26+ が確実なネイティブサポート、Safari は未対応でトランスパイル/ポリフィルが必要)を明記するか、代替として同じ処理を行う従来の `try/finally` フォールバック実装を本文に併記してください(参照すべきキーワード:`using` 構文、`try/finally` フォールバック、"This function can be copy-pasted into any project" 表現を修正)。src/hooks/useResizable.ts-185-195 (1)
185-195:⚠️ Potential issue | 🟠 Major公開した
setSizeが制約を素通りしますこの setter は
min/maxとaspectRatioの正規化を一切通さないので、外部呼び出しだけでフックの不変条件を崩せます。{ width: 1, height: 1 }のような値もそのまま入り、以後のaspectRatioRefまで汚染されます。handleResizeMoveと同じ正規化ロジックを共通化して、setSize/resetSize/ ドラッグ更新の全部で再利用した方が安全です。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/useResizable.ts` around lines 185 - 195, 公開されている setSize が min/max と aspectRatio の正規化を通さずフックの不変条件を壊せるので、handleResizeMove と同じ正規化ロジックを切り出して(例: normalizeSize 或いは applyConstraints として)setSizeState に渡す前に常に通すようにしてください;その共通関数は最小/最大値と aspectRatioRef を適用して新しいサイズを返し、setSize、resetSize、そして handleResizeMove の全てで使い、同一サイズなら早期リターンして state を更新しないようにしてください。.agents/skills/remotion-best-practices/rules/animations.md-12-20 (1)
12-20:⚠️ Potential issue | 🟠 Majorサンプルコードの import が不足しています
Line 16 と Line 18 で使っている
useVideoConfigとinterpolateが未import のため、このコード例はそのまま動作しません。修正案
-import { useCurrentFrame } from "remotion"; +import { interpolate, useCurrentFrame, useVideoConfig } from "remotion";🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/animations.md around lines 12 - 20, The sample component FadeIn uses useVideoConfig and interpolate but only imports useCurrentFrame; update the imports to include useVideoConfig and interpolate from "remotion" so the FadeIn component can access fps and compute opacity (i.e., add useVideoConfig and interpolate alongside useCurrentFrame in the import statement referenced by the FadeIn function)..agents/skills/remotion-best-practices/rules/trimming.md-15-20 (1)
15-20:⚠️ Potential issue | 🟠 Major
useVideoConfig()の戻り値の扱いが誤っていますLine 17 は
const { fps } = useVideoConfig();で取り出す必要があります。useVideoConfig()はオブジェクトを返すため、現状だとfpsがオブジェクト全体になり、Line 19 と Line 33 の算術演算が失敗します。差分案
-const fps = useVideoConfig(); +const { fps } = useVideoConfig();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/trimming.md around lines 15 - 20, useVideoConfig() returns an object, but the code currently assigns the whole object to fps; change the assignment to destructure the fps property (useVideoConfig -> const { fps } = useVideoConfig()) so arithmetic using fps (e.g., in Sequence from={-0.5 * fps} and any other calculations like the later usage around Line 33) operate on a number, not an object; update all occurrences that assume fps is a number to use the destructured fps from useVideoConfig()..agents/skills/remotion-promo-video-factory/scripts/verify-build.sh-14-19 (1)
14-19:⚠️ Potential issue | 🟠 Major
build:gifの存在判定が環境依存ですLine 14 の
rg依存とnpm run -s出力フォーマット依存で、ripgrep がインストールされていない環境ではset -euo pipefailにより即失敗します。npm run ... --if-presentに寄せる方が堅牢です。差分案
-if npm run -s | rg -q "^ build:gif"; then - echo "[3/3] GIF render" - npm run -s build:gif -else - echo "[3/3] GIF render skipped (build:gif not found)" -fi +echo "[3/3] GIF render (optional)" +npm run -s build:gif --if-present🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-promo-video-factory/scripts/verify-build.sh around lines 14 - 19, The script currently checks for the "build:gif" script by running "npm run -s" and piping to "rg", which is brittle (depends on ripgrep and npm output) and will fail under "set -euo pipefail"; change the logic to avoid parsing output and instead invoke npm directly with the built-in guard: run "npm run --if-present build:gif" (or the equivalent npm CLI flag) and use its exit behavior to decide logging, replacing the "npm run -s | rg -q" check and the subsequent "npm run -s build:gif" invocation so the script no longer depends on rg or npm output formatting..agents/skills/remotion-best-practices/rules/transparent-videos.md-91-93 (1)
91-93:⚠️ Potential issue | 🟠 MajorWebM (VP9) セクションで codec 値が不整合です。
Line [91] の
defaultCodecが"vp8"になっており、同セクションの意図(VP9)および Line [68] の CLI 例と矛盾しています。修正案
- defaultCodec: "vp8", + defaultCodec: "vp9",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/transparent-videos.md around lines 91 - 93, The defaultCodec value in the VP9 section is incorrect: change the property named defaultCodec (currently "vp8") to "vp9" so the configuration for defaultCodec, along with defaultVideoImageFormat and defaultPixelFormat, matches the VP9 section intent and the CLI example; locate and update the defaultCodec entry in this block to "vp9" to resolve the inconsistency..agents/skills/remotion-best-practices/rules/audio-visualization.md-194-197 (1)
194-197:⚠️ Potential issue | 🟠 MajordB変換で
log10(0)が発生し得ます。Line [195] は
value === 0のとき-Infinityになるため、後続の正規化結果が壊れます。修正案
-const scaled = frequencies.map((value) => { - const db = 20 * Math.log10(value); +const scaled = frequencies.map((value) => { + const safe = Math.max(value, 1e-6); + const db = 20 * Math.log10(safe); return (db - minDb) / (maxDb - minDb); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/audio-visualization.md around lines 194 - 197, The dB conversion in the frequencies.map callback can produce -Infinity when value === 0; update the scaled calculation inside frequencies.map (the callback using parameters value and local variable db) to guard against zero by substituting a small positive epsilon (e.g., Math.max(value, 1e-10) or Number.EPSILON) before calling Math.log10, then compute db and normalize as before; also ensure the normalization (using minDb and maxDb) won't divide by zero by handling the case maxDb === minDb (fallback to 0 or clamp) so the normalized result remains finite..agents/skills/remotion-best-practices/rules/parameters.md-47-53 (1)
47-53:⚠️ Potential issue | 🟠 Majorコンポーネント関数の引数が不足しており、サンプルコードが動作しません。
Line 47 でコンポーネント関数が引数を受け取らない
() => {と定義されているのに対し、Line 50 でprops.titleを参照しています。これにより型エラーおよび実行時エラーが発生します。関数シグネチャに
propsパラメータを追加してください。修正案
-const MyComponent: React.FC<z.infer<typeof MyCompositionSchema>> = () => { +const MyComponent: React.FC<z.infer<typeof MyCompositionSchema>> = (props) => { return ( <div> <h1>{props.title}</h1> </div> ); };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/parameters.md around lines 47 - 53, The component MyComponent currently declares no parameters but accesses props.title, causing type/runtime errors; update the function signature to accept a props parameter typed as z.infer<typeof MyCompositionSchema> (e.g., add a single parameter named props of that inferred type) so references like props.title are valid and type-checked..agents/skills/remotion-best-practices/rules/parameters.md-60-63 (1)
60-63:⚠️ Potential issue | 🟠 Majorimport 名と利用名が不一致です
Line 60 で
MycComponentを import していますが、Line 63 ではMyComponentを使用しています。このコード例をそのままコピーすると未定義参照エラーになります。修正
-import {MycComponent, MyCompositionSchema} from './MyComposition'; +import {MyComponent, MyCompositionSchema} from './MyComposition';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/parameters.md around lines 60 - 63, インポート名と利用名が一致していないため未定義参照が発生します:import側の MycComponent と JSX 内で使っている MyComponent を揃えてください(例えば RemotionRoot 内の Composition の component プロパティが参照する名前を import の MycComponent に合わせるか、逆に import を MyComponent に変更して整合性を取る)。該当シンボルは MycComponent / MyComponent とコンポーネント定義 RemotionRoot(および Composition の component プロパティ)です。.agents/skills/remotion-best-practices/rules/timing.md-88-92 (1)
88-92:⚠️ Potential issue | 🟠 Major
springの自己参照初期化によるドキュメントサンプルの実行エラーLine 88 の
const spring = spring({...})は実行不可能です。関数名と変数名が同一のため、右辺のspringは未定義状態となり ReferenceError が発生します。同ファイル内の他のサンプル (Line 75 のentrance、Line 100 のspringProgress) では正しく異なる変数名が使用されています。修正例:
修正案
-const spring = spring({ +const springProgress = spring({ frame, fps, durationInFrames: 40, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/timing.md around lines 88 - 92, const spring = spring({...}) は関数名と変数名が衝突して右辺の spring が未定義になるため ReferenceError を起こします。解決策は変数名を関数名と異なるものに変更することです(例: rename the variable to springValue or springAnim) — locate the problematic declaration `const spring = spring({...})` and rename the left-hand identifier to something like `springValue` (or match naming pattern used in `entrance` / `springProgress` samples), then update any subsequent references to use the new variable name so the call to the `spring(...)` function invokes the actual function rather than referencing the variable..agents/skills/remotion-best-practices/rules/maps.md-85-85 (1)
85-85:⚠️ Potential issue | 🟠 MajorMapbox style URL に不可視文字が混入しています
Line 85 の
'mapbox://styles/mapbox/standard'は先頭に不可視文字が入っており、コピペ時に URL 解析失敗の原因になります。ASCII のみの'mapbox://styles/mapbox/standard'に置き換えてください。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/maps.md at line 85, The Mapbox style string contains an invisible character before "mapbox://styles/mapbox/standard"; locate the style property where it is written as 'mapbox://styles/mapbox/standard' and replace it with the ASCII-only literal 'mapbox://styles/mapbox/standard' so the URL parses correctly when copied/pasted..agents/skills/remotion-best-practices/rules/maps.md-46-46 (1)
46-46:⚠️ Potential issue | 🟠 Major環境変数サンプルの代入記号が誤っています
Line 46 は
==になっており、.envとして無効です。=に修正してください。修正例
-REMOTION_MAPBOX_TOKEN==pk.your-mapbox-access-token +REMOTION_MAPBOX_TOKEN=pk.your-mapbox-access-token🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/maps.md at line 46, The environment variable sample uses a double-equals operator; update the sample so REMOTION_MAPBOX_TOKEN is assigned with a single equals sign (change REMOTION_MAPBOX_TOKEN==pk.your-mapbox-access-token to REMOTION_MAPBOX_TOKEN=pk.your-mapbox-access-token) in the maps.md rule so it becomes a valid .env assignment..agents/skills/remotion-best-practices/rules/light-leaks.md-10-10 (1)
10-10:⚠️ Potential issue | 🟠 Majorバージョンアップを即時実行する文言は避けてください
Line 10 の
npx remotion upgradeは、明示許可なしの依存更新を誘導します。upgradeの直接案内ではなく、「現在バージョン確認のみ」または「明示承認後に実施」に変更するのが安全です。修正例
-This only works from Remotion 4.0.415 and up. Use `npx remotion versions` to check your Remotion version and `npx remotion upgrade` to upgrade your Remotion version. +This only works from Remotion 4.0.415 and up. Use `npx remotion versions` to check your current version. +If an upgrade is needed, perform it only after explicit approval in your project workflow.Based on learnings: Do not upgrade package versions without explicit permission.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/light-leaks.md at line 10, Update the guidance that currently instructs users to run "npx remotion upgrade" (line containing `npx remotion upgrade`) to avoid prompting unapproved dependency updates; change the text to only show how to check the current Remotion version (keep `npx remotion versions`) and state that upgrades should be performed only after explicit approval or follow your project's upgrade process (e.g., "confirm with your team/maintainer before running an upgrade" or "if approved, run `npx remotion upgrade`"); ensure the revised sentence replaces the direct upgrade imperative with a conditional/approval-first phrasing..agents/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx-1-108 (1)
1-108:⚠️ Potential issue | 🟠 MajorPrettier違反が大量にあり、lint失敗の原因になります
Line [1] から Line [108] までフォーマット不一致が連続しているため、このままだとCIで失敗します。プロジェクト設定の Prettier でこのファイルを整形してください。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx around lines 1 - 108, ファイル全体がプロジェクトの Prettier 設定に合っておらず CI の lint を通らないため、.agents/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx をプロジェクトの Prettier 設定で整形してください(例: 実行コマンド: npx prettier --write <file> またはエディタの「Format Document」)。対象コードには Highlight コンポーネント、MyAnimation コンポーネント、および定数 (COLOR_BG, COLOR_TEXT, COLOR_HIGHLIGHT, FULL_TEXT, HIGHLIGHT_WORD, etc.) が含まれているので、整形後にインポート行や JSX のインデント、カンマ・セミコロン等の一貫性を確認してコミットしてください。.agents/skills/remotion-promo-video-factory/scripts/capture-frames.sh-15-24 (1)
15-24:⚠️ Potential issue | 🟠 Major
--project-dir指定時に出力先が意図せず変わる不具合がありますLine [15] で相対
OUTPUT_DIRを作成したあと、Line [22] でcdしているため、Line [23] の書き込み先が別ディレクトリ基準になります。project-dirを使うとフレーム保存先がずれる/失敗する可能性があります。💡 提案修正
mkdir -p "${OUTPUT_DIR}" +OUTPUT_DIR="$(cd "${OUTPUT_DIR}" && pwd)" IFS=',' read -r -a FRAMES <<< "${FRAME_LIST}" for frame in "${FRAMES[@]}"; do frame_trimmed="$(echo "$frame" | xargs)" out_file="${OUTPUT_DIR}/frame-$(printf "%03d" "${frame_trimmed}").png"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-promo-video-factory/scripts/capture-frames.sh around lines 15 - 24, The script creates OUTPUT_DIR before changing directory into PROJECT_DIR, so when you cd into PROJECT_DIR the subsequent npx remotion still writes relative to PROJECT_DIR and can misplace outputs; fix by resolving OUTPUT_DIR to an absolute path (or otherwise prefix out_file with an absolute path) before the for loop and before the cd so that out_file uses the absolute OUTPUT_DIR when calling npx remotion still; update the capture-frames.sh logic around OUTPUT_DIR, FRAMES parsing, and the npx remotion still invocation to reference that absolute path (alternatively, create OUTPUT_DIR after cd but ensure it's the intended location)..agents/skills/remotion-best-practices/rules/videos.md-41-41 (1)
41-41:⚠️ Potential issue | 🟠 Major
trimBefore/trimAfterの単位説明を修正してくださいLine 41 の「Values are in seconds」は誤りです。Remotion公式ドキュメントによると、これらのプロパティは フレーム単位 です。秒単位と記載されたままでは、利用者が秒数を直接渡して意図しないトリミングが発生します。
修正案
-Use `trimBefore` and `trimAfter` to remove portions of the video. Values are in seconds. +Use `trimBefore` and `trimAfter` to remove portions of the video. Values are in frames.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/videos.md at line 41, The documentation line incorrectly states that trimBefore and trimAfter are in seconds; update the description for the properties trimBefore and trimAfter to state that their values are in frames (frame units) instead of seconds, and adjust any explanatory text or examples on that line to reflect frame-based values so users do not pass seconds by mistake.src/features/gameCommentary/gameCommentaryTypes.ts-65-96 (1)
65-96:⚠️ Potential issue | 🟠 Majorクランプ関数が
NaNを素通りします。Line 65-96 のクランプ関数は
NaN入力を処理していません。NaN の比較結果はすべてfalseであるため、そのまま返されます。実装上の懸念:
src/components/settings/gameCommentarySettings.tsxLine 417-419 で、parseInt()の結果をisNaNチェックなしにclampBackgroundAnalysisInterval()に渡しています- Line 306 では
clampContextCount()に対してif (!isNaN(value))ガード があり、対照的です
Number.isFinite()ガードで防いでください。DEFAULT_GAME_COMMENTARY_CONFIGの値をフォールバックとして使用する提案は妥当です。🛠 提案差分
+function clampNumber(value: number, min: number, max: number, fallback: number) { + if (!Number.isFinite(value)) return fallback + if (value < min) return min + if (value > max) return max + return value +} + // Validate and clamp capture interval value export function clampCaptureInterval(value: number): number { - if (value < GAME_COMMENTARY_INTERVAL.MIN) return GAME_COMMENTARY_INTERVAL.MIN - if (value > GAME_COMMENTARY_INTERVAL.MAX) return GAME_COMMENTARY_INTERVAL.MAX - return value + return clampNumber( + value, + GAME_COMMENTARY_INTERVAL.MIN, + GAME_COMMENTARY_INTERVAL.MAX, + DEFAULT_GAME_COMMENTARY_CONFIG.gameCommentaryCaptureInterval + ) } // Validate and clamp context count value export function clampContextCount(value: number): number { - if (value < GAME_COMMENTARY_CONTEXT_COUNT.MIN) - return GAME_COMMENTARY_CONTEXT_COUNT.MIN - if (value > GAME_COMMENTARY_CONTEXT_COUNT.MAX) - return GAME_COMMENTARY_CONTEXT_COUNT.MAX - return value + return clampNumber( + value, + GAME_COMMENTARY_CONTEXT_COUNT.MIN, + GAME_COMMENTARY_CONTEXT_COUNT.MAX, + DEFAULT_GAME_COMMENTARY_CONFIG.gameCommentaryContextCount + ) } // Validate and clamp video delay value export function clampVideoDelay(value: number): number { - if (value < GAME_COMMENTARY_VIDEO_DELAY.MIN) - return GAME_COMMENTARY_VIDEO_DELAY.MIN - if (value > GAME_COMMENTARY_VIDEO_DELAY.MAX) - return GAME_COMMENTARY_VIDEO_DELAY.MAX - return value + return clampNumber( + value, + GAME_COMMENTARY_VIDEO_DELAY.MIN, + GAME_COMMENTARY_VIDEO_DELAY.MAX, + DEFAULT_GAME_COMMENTARY_CONFIG.gameCommentaryVideoDelay + ) } // Validate and clamp background analysis interval value export function clampBackgroundAnalysisInterval(value: number): number { - if (value < GAME_COMMENTARY_BACKGROUND_ANALYSIS_INTERVAL.MIN) - return GAME_COMMENTARY_BACKGROUND_ANALYSIS_INTERVAL.MIN - if (value > GAME_COMMENTARY_BACKGROUND_ANALYSIS_INTERVAL.MAX) - return GAME_COMMENTARY_BACKGROUND_ANALYSIS_INTERVAL.MAX - return value + return clampNumber( + value, + GAME_COMMENTARY_BACKGROUND_ANALYSIS_INTERVAL.MIN, + GAME_COMMENTARY_BACKGROUND_ANALYSIS_INTERVAL.MAX, + DEFAULT_GAME_COMMENTARY_CONFIG.gameCommentaryBackgroundAnalysisInterval + ) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/gameCommentary/gameCommentaryTypes.ts` around lines 65 - 96, All clamp* functions (clampCaptureInterval, clampContextCount, clampVideoDelay, clampBackgroundAnalysisInterval) currently pass NaN through because comparisons with NaN are false; update each to first check Number.isFinite(value) and if false return the corresponding default from DEFAULT_GAME_COMMENTARY_CONFIG (e.g., DEFAULT_GAME_COMMENTARY_CONFIG.captureInterval, contextCount, videoDelay, backgroundAnalysisInterval) before performing MIN/MAX clamping so callers like clampBackgroundAnalysisInterval(parseInt(...)) are safe without their own isNaN guards.src/components/menu.tsx-280-324 (1)
280-324:⚠️ Potential issue | 🟠 MajorYouTube モード時の既存 UI 変更は切り分けたほうが安全です。
Line 280 以降で
!youtubeModeガードを外したため、ゲーム実況と無関係でも capture / webcam / image ボタンが常時表示されます。今回の目的がゲーム実況用操作の追加なら、少なくともgameCommentaryEnabled時だけ露出するなど、既存 UX への影響は限定したほうがよいです。Based on learnings, Do not change the existing UI/UX design without explicit permission
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/menu.tsx` around lines 280 - 324, The new capture/webcam/image buttons are being rendered unconditionally; restrict their visibility to the game-commentary/YouTube mode by wrapping the IconButton + input block with a guard (e.g., check gameCommentaryEnabled or youtubeMode) so they only render when that feature is enabled; update the conditional around the block that uses toggleCapture, toggleWebcam, imageFileInputRef and the isMultiModalAvailable(...) check (which uses selectAIService, selectAIModel, enableMultiModal, customModel) and ensure readers still set homeStore.modalImage only when the image input is used.src/components/capture.tsx-52-63 (1)
52-63:⚠️ Potential issue | 🟠 Major遅延実況が有効でも、実況用キャプチャ元がライブ映像のままです。
Line 53 で登録しているコールバックは常に
videoRef.currentを JPEG 化しています。これだとgameCommentaryVideoDelayを有効にしても、ゲーム実況の解析対象だけは遅延前の現在フレームになり、表示中の映像と実況対象がずれます。CaptureService側は遅延キャンバス/遅延フレームを優先して読むようにしないと、今回の delay 機能が実況には効きません。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/capture.tsx` around lines 52 - 63, The registered capture callback always reads from videoRef.current, so when gameCommentaryVideoDelay is enabled the commentary capture bypasses the delay and captures the live frame; update the callback registered via CaptureService.getInstance().registerCaptureFunction to first check for and prefer the delay-provided frame/canvas from CaptureService (e.g., a delayedCanvas/delayedFrame API or a getDelayedFrame/getDelayedCanvas method) when gameCommentaryVideoDelay is enabled, falling back to videoRef.current only if no delayed frame is available; ensure you still validate readiness (readyState and canvas context) and return the JPEG dataURL as before.src/components/capture.tsx-137-145 (1)
137-145:⚠️ Potential issue | 🟠 Major遅延フレームループが停止後も残り、cleanup 後の
ImageBitmapもリークします。Line 137 の effect は
isVideoDelayedが変わらない限り生き続けるので、画面共有を止めてもrequestAnimationFrameが回り続けます。加えて Line 170 のcreateImageBitmap()は cleanup 後にも resolve し得るため、破棄済みバッファへ再度bitmapを積んでリークします。キャプチャ状態も終了条件に含め、dispose 済みなら解放して捨ててください。Also applies to: 170-179, 221-228
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/capture.tsx` around lines 137 - 145, The effect that drives the delayed-frame capture (inside useEffect) can continue running after capture stops and createImageBitmap() can resolve after cleanup, leaking ImageBitmap objects; fix this by tracking an active flag and cancelling the RAF loop on cleanup and when capture stops, and by checking that flag after createImageBitmap resolves before pushing to frameBuffer — if inactive, immediately call bitmap.close() instead of storing it. Specifically, in the useEffect surrounding CAPTURE_FPS/CAPTURE_INTERVAL_MS/frameBuffer/offscreen/offCtx, store the requestAnimationFrame id and clear it on cleanup, add a local boolean like let active = true and set active = false in cleanup, include capture-running state (e.g., isVideoDelayed && videoDelaySeconds > 0 && captureActive) in the effect conditions, and after each createImageBitmap(...) resolution, check active (or capture state) before pushing to frameBuffer; if not active call bitmap.close() to release resources.src/features/messages/speakQueue.ts-83-95 (1)
83-95:⚠️ Potential issue | 🟠 Major
stopSessionが対象外セッションの待機タスクまで消しています。Line 84-86 で停止対象セッションだけを除外したあと、Line 95 の
clearQueue()で残っている別セッションのタスクまで全消去されています。新しいセッションの音声がすでに積まれているケースだと、stopSessionが実質的にグローバル stop になってしまいます。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/messages/speakQueue.ts` around lines 83 - 95, The stopSession implementation currently removes tasks for the given sessionId but then calls instance.clearQueue(), which deletes all remaining tasks across sessions; change it to only clear tasks for the session being stopped (e.g., filter instance.queue to remove tasks with sessionId === sessionId or add a new method like clearSessionQueue(sessionId)) and avoid calling the global clearQueue() that knocks out other sessions; also ensure the flags manipulated (instance.stopped, instance.isProcessing, SpeakQueue.stopTokenCounter) are applied only in the context of the stopped session (or tied to a per-session token) so other sessions continue processing normally.src/features/gameCommentary/captureService.ts-34-55 (1)
34-55:⚠️ Potential issue | 🟠 Major
captureFrameメソッドの実装では画像リサイズと品質圧縮がほぼ機能しません。
img.src = rawの直後にimg.width/img.heightを読み込んでいますが、HTML仕様に基づきHTMLImageElementの読み込みは非同期であり、この時点では幅と高さは 0 です。そのため Line 43 の条件if (img.width <= maxWidth)は常に真(0 ≤ maxWidth)となり、元の未圧縮データ URL が返されます。結果としてmaxWidth引数とquality引数がまったく適用されず、高解像度のまま送信されます。このメソッドは非同期化し、
img.onloadまたはこれに相当する方法で画像の読み込み完了後にdrawImageを呼び出す必要があります。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/gameCommentary/captureService.ts` around lines 34 - 55, The captureFrame implementation currently reads img.width/img.height synchronously so resizing never happens; change captureFrame (the function named captureFrame) to be asynchronous (return Promise<string | null>), create the Image, set img.src, and await its load (using img.onload wrapped in a Promise or equivalent) before checking img.width/img.height and performing canvas resizing and canvas.toDataURL with the provided quality; also handle img.onerror to reject/return null and keep existing fallbacks (return raw when no resizing needed) and ensure getContext('2d') null-check remains.src/features/gameCommentary/analyzeGameCommentaryScene.ts-18-20 (1)
18-20:⚠️ Potential issue | 🟠 Major背景解析リクエストも中断できるようにしてください。
この関数は
AbortSignalを受け取れないため、実況停止や割り込み後も背景解析の AI リクエストが最後まで走り続けます。結果は捨ててもトークン消費と接続保持は残るので、AIChatResponseStreamOptionsを受け取ってgetAIChatResponseStream(messages, options)に流した方が安全です。🔧 修正例
+import type { AIChatResponseStreamOptions } from '@/features/chat/aiChatFactory' import { getAIChatResponseStream } from '@/features/chat/aiChatFactory' import { THINKING_MARKER } from '@/features/chat/vercelAIChat' import { Message } from '@/features/messages/messages' import settingsStore from '@/features/stores/settings' @@ export async function analyzeGameCommentaryScene( - imageData: string + imageData: string, + options: AIChatResponseStreamOptions = {} ): Promise<string | null> { @@ - const stream = await getAIChatResponseStream(messages) + const stream = await getAIChatResponseStream(messages, options)Also applies to: 48-49
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/gameCommentary/analyzeGameCommentaryScene.ts` around lines 18 - 20, analyzeGameCommentaryScene should accept AIChatResponseStreamOptions (or at least an AbortSignal) and forward it to getAIChatResponseStream so background AI requests can be aborted; update the function signature export async function analyzeGameCommentaryScene(imageData: string, options?: AIChatResponseStreamOptions | { signal?: AbortSignal }) : Promise<string | null> and pass options (or options.signal) into getAIChatResponseStream(messages, options), and apply the same change to the other similar call site(s) in this file so all AI chat stream calls support cancellation.src/components/common/VideoDisplay.tsx-75-76 (1)
75-76:⚠️ Potential issue | 🟠 Major
hideVideoDisplayが背景表示まで消しています。
showBackgroundVideoを!hideVideoDisplayで絞っているので、背景モードで「非表示」を押すとプレビュー非表示ではなく背景そのものが消えます。HideVideoDisplayという挙動より一段強く、既存の背景表示 UX を変えてしまうので、非表示はフローティングプレビュー/UI だけに限定した方がよいです。Based on learnings: Do not change the existing UI/UX design without explicit permission.Also applies to: 486-522
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/components/common/VideoDisplay.tsx` around lines 75 - 76, The current logic makes hideVideoDisplay also hide the background video because showBackgroundVideo is conditioned with !hideVideoDisplay; change the condition so background mode only depends on useVideoAsBackground (e.g., showBackgroundVideo = useVideoAsBackground) and keep hideVideoDisplay affecting only the floating preview (showFloatingPreview = !useVideoAsBackground && !hideVideoDisplay); update the same pattern wherever similar logic appears (the other block referenced around the same diff) to preserve existing background-UX while allowing hideVideoDisplay to only hide the floating preview/UI.src/features/gameCommentary/generateGameCommentary.ts-100-106 (1)
100-106:⚠️ Potential issue | 🟠 Major
THINKING_MARKERの除外がチャンク境界に依存しています。
reader.read()の分割単位は任意なので、マーカー自体や思考本文が複数チャンクに分かれるとstartsWith()では取り切れません。結果として思考テキストが実況本文に混ざる可能性があります。ストリーム全体で状態を持つデコーダにするか、同じ処理をanalyzeGameCommentaryScene.tsと共通化した方が安全です。Based on learnings: Verify that no duplicate implementations exist before adding new features.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/gameCommentary/generateGameCommentary.ts` around lines 100 - 106, The code relies on value.startsWith(THINKING_MARKER) inside the reader.read() loop (in generateGameCommentary.ts), which fails when the marker or thought content is split across stream chunks; change to a stateful stream decoder that keeps a running buffer across reads and searches for THINKING_MARKER anywhere in the buffered text (or reuse the same decoder/utility used by analyzeGameCommentaryScene.ts if present) so you correctly detect and strip the marker and the entire thought block even when split across chunks; update logic around reader.read(), fullText accumulation, and the marker-handling path to operate on the buffer and only emit non-thinking text to fullText.src/hooks/useGameCommentaryMode.ts-350-358 (1)
350-358:⚠️ Potential issue | 🟠 Major
maxPastMessages = 0で履歴が無制限になります。
slice(0)は 0 件ではなく全件を返すため、履歴を送らない設定でも毎回すべてのチャットをプロンプトに載せてしまいます。コスト増とコンテキスト肥大化につながるので、0 以下のときは空配列を返す分岐にしてください。🔧 修正例
- const recentMessages = chatLog - .filter((m) => m.role === 'user' || m.role === 'assistant') - .slice(maxPastMessages > 0 ? -maxPastMessages : 0) - .map((m) => ({ - role: m.role, - content: typeof m.content === 'string' ? m.content : '', - })) + const recentMessages = + maxPastMessages > 0 + ? chatLog + .filter((m) => m.role === 'user' || m.role === 'assistant') + .slice(-maxPastMessages) + .map((m) => ({ + role: m.role, + content: typeof m.content === 'string' ? m.content : '', + })) + : []🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/useGameCommentaryMode.ts` around lines 350 - 358, The recentMessages calculation in useGameCommentaryMode uses slice(maxPastMessages > 0 ? -maxPastMessages : 0) which causes slice(0) to return the full chatLog when maxPastMessages is 0; change the logic so that when maxPastMessages is <= 0 you return an empty array instead of slicing, e.g. branch before mapping: if maxPastMessages <= 0 set recentMessages = [] else take the last maxPastMessages from chatLog (using slice(-maxPastMessages)) and then map; update the code that builds recentMessages (references: maxPastMessages, chatLog, recentMessages) accordingly.src/hooks/useGameCommentaryMode.ts-291-306 (1)
291-306:⚠️ Potential issue | 🟠 Major
scheduleNext()の依存配列にtriggerCommentaryを追加してください。
scheduleNext()内のsetTimeoutでtriggerCommentary()を呼び出していますが、scheduleNext()の依存配列[clearTimers, getEffectiveCaptureInterval]にtriggerCommentaryが含まれていません。そのため、triggerCommentaryが依存するgameCommentaryResizeWidthやgameCommentaryImageQualityなどの設定が変更されて新しいバージョンが作成されても、スケジュール済みのタイマーは古いバージョンの関数を使い続けます。依存配列にtriggerCommentaryを追加するか、ref経由で最新の関数を参照する仕組みに変更してください。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/hooks/useGameCommentaryMode.ts` around lines 291 - 306, scheduleNext currently sets a timeout that calls triggerCommentary but its dependency array only lists clearTimers and getEffectiveCaptureInterval, so update scheduleNext to include triggerCommentary in its dependency array (i.e. [clearTimers, getEffectiveCaptureInterval, triggerCommentary]) so the timer uses the latest triggerCommentary; alternatively, replace direct triggerCommentary usage inside setTimeout with a mutable ref (e.g., triggerCommentaryRef.current) and keep the existing dependencies, ensuring countdownRef and timerRef still clear/reset with clearTimers.
🧹 Nitpick comments (14)
.agents/skills/sync-translations/SKILL.md (2)
39-41: コードブロックに言語指定子を追加してください静的解析ツールが指摘しているように、コードブロックには言語指定子を追加することが推奨されます。
📝 修正案
-``` +```text locales/ja/translation.json</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In @.agents/skills/sync-translations/SKILL.md around lines 39 - 41, Update the
fenced code block that currently contains "locales/ja/translation.json" in
.agents/skills/sync-translations/SKILL.md to include a language specifier (e.g.,
change the openingtotext) so the code block is explicitly marked;
locate the block with the exact content locales/ja/translation.json and replace
its fence accordingly.</details> --- `84-86`: **コードブロックに言語指定子を追加してください** コマンド実行例のコードブロックには言語指定子(`bash`または`shell`)を追加してください。 <details> <summary>📝 修正案</summary> ```diff -``` +```bash /sync-translations ``` ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against the current code and only fix it if needed.
In @.agents/skills/sync-translations/SKILL.md around lines 84 - 86, The fenced
code block showing the command "/sync-translations" lacks a language tag; update
that triple-backtick block to include a shell language specifier (e.g.,bash orshell) so the command renders with proper syntax highlighting—locate the
fenced block containing "/sync-translations" in SKILL.md and change the opening
fence tobash (orshell).</details> </blockquote></details> <details> <summary>.agents/skills/remotion-best-practices/rules/charts.md (1)</summary><blockquote> `17-31`: **変数名の衝突に注意してください。** Line 29 の `height: height * item.value` において、`height` という変数名が2回使用されています(CSSプロパティ名と `useVideoConfig()` から取得した変数)。これは技術的には動作しますが、可読性のため、異なる変数名を使用することを検討してください(例:`barHeight` や `computedHeight`)。 <details> <summary>💡 可読性向上の提案</summary> ```diff const STAGGER_DELAY = 5; const frame = useCurrentFrame(); -const { fps } = useVideoConfig(); +const { fps, height: videoHeight } = useVideoConfig(); const bars = data.map((item, i) => { const height = spring({ frame, fps, delay: i * STAGGER_DELAY, config: { damping: 200 }, }); - return <div style={{ height: height * item.value }} />; + return <div style={{ height: `${height * item.value * videoHeight}px` }} />; }); ``` または、棒の高さ計算を別の変数名で行う: ```diff const bars = data.map((item, i) => { - const height = spring({ + const progress = spring({ frame, fps, delay: i * STAGGER_DELAY, config: { damping: 200 }, }); - return <div style={{ height: height * item.value }} />; + const barHeight = progress * item.value; + return <div style={{ height: barHeight }} />; }); ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/charts.md around lines 17 - 31, The code reuses the identifier height for both the spring result and the CSS property which hurts readability; in the block that creates bars (variables STAGGER_DELAY, frame from useCurrentFrame, fps from useVideoConfig, and the bars map), rename the spring result (currently height) to a distinct name (e.g., barHeight or computedHeight) and use that new name when computing the style (height: barHeight * item.value) so the spring value and the CSS property are not conflated. ``` </details> </blockquote></details> <details> <summary>.agents/skills/remotion-best-practices/rules/transcribe-captions.md (1)</summary><blockquote> `55-65`: **プレースホルダーファイル名をより明確にすることを検討してください。** `audio123.wav` や `captions123.json` のようなファイル名は、実際の使用例として混乱を招く可能性があります。より明確なプレースホルダー(例:`audio.wav` や `captions.json`、または `input-audio.wav` と `output-captions.json`)を使用することで、ドキュメントの理解しやすさが向上します。 <details> <summary>💡 より明確な例への改善案</summary> ```diff - inputPath: "/path/to/audio123.wav", + inputPath: "/path/to/input-audio.wav", tokenLevelTimestamps: true, }); // Optional: Apply our recommended postprocessing const { captions } = toCaptions({ whisperCppOutput, }); // Write it to the public/ folder so it can be fetched from Remotion -fs.writeFileSync("captions123.json", JSON.stringify(captions, null, 2)); +fs.writeFileSync("output-captions.json", JSON.stringify(captions, null, 2)); ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/transcribe-captions.md around lines 55 - 65, Change confusing placeholder filenames to clearer, consistent examples: update the inputPath value (referenced in the diff and the variable inputPath) from "audio123.wav" to something like "input-audio.wav" or "audio.wav", and update the fs.writeFileSync target (where captions from toCaptions(whisperCppOutput) are written) from "captions123.json" to "output-captions.json" or "captions.json"; keep the variable names whisperCppOutput, toCaptions, and captions unchanged so the example still shows how the transcription output flows into fs.writeFileSync. ``` </details> </blockquote></details> <details> <summary>.agents/skills/remotion-best-practices/rules/gifs.md (1)</summary><blockquote> `2-3`: **frontmatter の `name` をファイル名に揃えると管理しやすいです。** `gifs.md` に対して `name: gif` だと参照時に揺れやすいので、`gifs` に統一するのをおすすめします。 <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-best-practices/rules/gifs.md around lines 2 - 3, frontmatter の name がファイル名と一致しておらず参照時に混乱するので、gifs.md の frontmatter 内の name: gif を name: gifs に変更してファイル名と一致させてください(検索や参照で使われる識別子である frontmatter の name を統一することで管理が楽になります)。 ``` </details> </blockquote></details> <details> <summary>.agents/skills/remotion-promo-video-factory/SKILL.md (1)</summary><blockquote> `123-126`: **最終検証コマンドを package manager 非依存の表現にすると再利用しやすいです。** 現状は npm 固定なので、`<pm> run ...` 形式や npm/bun/yarn/pnpm 併記にするとチーム適用範囲が広がります。 <details> <summary>例(記述改善)</summary> ```diff -1. `npx tsc --noEmit` -2. `npm run -s build` -3. `npm run -s build:gif` if available +1. `npx tsc --noEmit` +2. `<pm> run -s build` (例: npm / bun / yarn / pnpm) +3. `<pm> run -s build:gif` if available ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In @.agents/skills/remotion-promo-video-factory/SKILL.md around lines 123 - 126, 現状の npm 固定コマンド(例: "npx tsc --noEmit", "npm run -s build", "npm run -s build:gif")はパッケージマネージャ依存なので、再利用性を高めるためにパッケージマネージャ非依存の表現へ変更してください。具体的には SKILL.md の該当コマンドリストを "<pm> run -s build" のようなプレースホルダ形式に置き換えるか、"npm / yarn / pnpm / bun それぞれのコマンドを併記" のいずれかで記述を更新し、最後の検証手順("Render final mp4...")も同様に pm 非依存または各 PM 対応例を追記してチームで使える汎用的な記述にしてください。 ``` </details> </blockquote></details> <details> <summary>src/utils/mediaDisplay.ts (1)</summary><blockquote> `7-14`: **無効入力時のフォールバックは最小サイズ保証を入れると安全です。** Line 13 で 0/負値が返る可能性があるため、最低1pxに寄せる防御を推奨します。 <details> <summary>💡 提案差分</summary> ```diff if ( sourceWidth <= 0 || sourceHeight <= 0 || maxWidth <= 0 || maxHeight <= 0 ) { - return { width: maxWidth, height: maxHeight } + return { + width: Math.max(1, maxWidth), + height: Math.max(1, maxHeight), + } } ``` </details> <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@src/utils/mediaDisplay.ts` around lines 7 - 14, The fallback path in src/utils/mediaDisplay.ts currently returns maxWidth/maxHeight directly when inputs are non-positive; change that to ensure a minimum of 1px by clamping both width and height with Math.max(1, maxWidth) and Math.max(1, maxHeight) before returning, so the function never yields 0 or negative dimensions (update the return in the conditional that currently returns the raw maxWidth/maxHeight). ``` </details> </blockquote></details> <details> <summary>src/components/settings/index.tsx (1)</summary><blockquote> `21-21`: **import は `@/*` エイリアスへ統一してください。** この追加行だけ相対パスになっており、リポジトリの import ルールとずれています。 <details> <summary>差分案</summary> ```diff -import GameCommentarySettings from './gameCommentarySettings' +import GameCommentarySettings from '@/components/settings/gameCommentarySettings' ``` </details> As per coding guidelines, `src/**/*.{ts,tsx,js,jsx}`: Use path alias `@/*` for imports instead of relative paths to reference `./src/*`. <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@src/components/settings/index.tsx` at line 21, The import of GameCommentarySettings uses a relative path ('./gameCommentarySettings'); update that import to use the project alias syntax starting with '@/...' instead so it follows the repository import rule (replace the './gameCommentarySettings' import with an equivalent '@/...' alias import for GameCommentarySettings). ``` </details> </blockquote></details> <details> <summary>src/features/chat/vercelAIChat.ts (1)</summary><blockquote> `10-10`: **新規 import は `@/` エイリアスで統一してください。** Line 10 だけ相対 import を追加していて、この PR で規約違反を増やしています。 As per coding guidelines, `src/**/*.{ts,tsx,js,jsx}`: Use path aliases `@/*` → `./src/*` in all TypeScript and JavaScript imports <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@src/features/chat/vercelAIChat.ts` at line 10, The import in vercelAIChat.ts uses a relative path; change it to the project alias style by replacing the relative import of AIChatResponseStreamOptions with the '@/...' alias (e.g., import type { AIChatResponseStreamOptions } from '@/features/chat/aiChatFactory') so the symbol AIChatResponseStreamOptions is imported via the alias and matches the repo's src import convention. ``` </details> </blockquote></details> <details> <summary>src/features/chat/openAIAudioChat.ts (1)</summary><blockquote> `14-14`: **新しい import は `@/` エイリアスへ寄せてください。** `./aiChatFactory` を増やすより、`@/features/chat/aiChatFactory` に揃えたほうがこの配下の import ルールと一貫します。 As per coding guidelines, "Use path alias `@/*` for imports instead of relative paths to reference `./src/*`". <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@src/features/chat/openAIAudioChat.ts` at line 14, The import in openAIAudioChat.ts uses a relative path; replace it with the project path alias by importing AIChatResponseStreamOptions from "@/features/chat/aiChatFactory" instead of "./aiChatFactory" so it follows the repository's alias convention and keeps imports consistent under this feature (update the import statement that references AIChatResponseStreamOptions accordingly). ``` </details> </blockquote></details> <details> <summary>src/__tests__/features/messages/speakCharacterConcurrency.test.ts (1)</summary><blockquote> `2-66`: **テスト側の import / mock パスも `@/` に揃えてください。** 深い相対パスが多く、ファイル移動で壊れやすいですし、`speakCharacter.ts` 側の import 表記ともずれます。alias に揃えると mock 対象も追いやすくなります。 As per coding guidelines, "Use path alias `@/*` for imports instead of relative paths to reference `./src/*`". <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@src/__tests__/features/messages/speakCharacterConcurrency.test.ts` around lines 2 - 66, Update all jest.mock and import statements in this test to use the project path alias (`@/`...) instead of deep relative paths: replace occurrences like '../../../features/stores/settings', '../../../features/messages/speakQueue', '../../../utils/wait', '../../../features/messages/synthesizeVoiceVoicevox', and the import of speakCharacter from '../../../features/messages/speakCharacter' with their corresponding '@/...' alias modules so mocks align with speakCharacter's imports and are resilient to file moves; ensure each mocked symbol (e.g., getState, SpeakQueue class, wait, synthesizeVoiceVoicevoxApi, etc.) keeps the same mock shape and names when switching to the alias paths. ``` </details> </blockquote></details> <details> <summary>src/features/chat/difyChat.ts (1)</summary><blockquote> `5-5`: **新しい import は `@/` エイリアスに揃えてください。** ここだけ `./aiChatFactory` の相対パスが増えると、移動時に壊れやすくなります。`@/features/chat/aiChatFactory` に合わせたほうが、このディレクトリの import ルールと揃います。 As per coding guidelines, "Use path alias `@/*` for imports instead of relative paths to reference `./src/*`". <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@src/features/chat/difyChat.ts` at line 5, Update the import for AIChatResponseStreamOptions to use the project path alias instead of a relative path: replace the relative import of AIChatResponseStreamOptions from "./aiChatFactory" with the aliased import from "@/features/chat/aiChatFactory" so the symbol AIChatResponseStreamOptions is imported via the `@/` alias and matches the directory's import convention. ``` </details> </blockquote></details> <details> <summary>src/components/settings/gameCommentarySettings.tsx (1)</summary><blockquote> `7-10`: **新規 import は `@/` エイリアスに揃えてください。** Line 10 だけ相対 import になっていて、このPRで追加したコードが既存ルールから外れています。`@/components/toggleSwitch` のようなエイリアス参照に合わせたいです。 As per coding guidelines "Use path alias `@/*` for imports instead of relative paths to reference `./src/*`". <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@src/components/settings/gameCommentarySettings.tsx` around lines 7 - 10, The import for ToggleSwitch uses a relative path ('../toggleSwitch') which violates the project's alias rule; update the import to use the path alias (e.g., import ToggleSwitch from '@/components/toggleSwitch') so the symbol ToggleSwitch is imported via the '@/...' alias consistent with other imports like settingsStore and homeStore. ``` </details> </blockquote></details> <details> <summary>src/__tests__/features/gameCommentary/generateGameCommentary.test.ts (1)</summary><blockquote> `103-118`: **`console.error` の差し替えは `spyOn` / `mockRestore` に寄せたほうが安全です。** この書き方だと途中の `await` やアサーションで落ちた場合に復元まで到達せず、後続テストまでグローバルな `console.error` が汚染されます。`jest.spyOn(console, 'error')` を使って `mockRestore()` で戻す形にしてください。 <details> <summary>🤖 Prompt for AI Agents</summary> ``` Verify each finding against the current code and only fix it if needed. In `@src/__tests__/features/gameCommentary/generateGameCommentary.test.ts` around lines 103 - 118, Replace the direct reassignment of console.error in the test for generateGameCommentary with jest.spyOn to avoid global contamination: use jest.spyOn(console, 'error') before mocking getAIChatResponseStream to reject with abortError, assert expectations, and finally call mockRestore() on the spy to ensure console.error is restored even if the test fails; reference the generateGameCommentary test block and the getAIChatResponseStream mock when making the change. ``` </details> </blockquote></details> </blockquote></details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/hooks/useGameCommentaryMode.ts`:
- Line 66: gameCommentaryResizeWidth is using a falsy check
(ss.gameCommentaryResizeWidth || 1024) which treats 0 as unset and overrides the
intended "no resize" value; change the assignment in useGameCommentaryMode (the
gameCommentaryResizeWidth binding that reads from ss) to only default when the
setting is undefined or null (e.g., use nullish coalescing or an explicit
undefined check) so that a configured 0 is preserved while still defaulting to
1024 when truly not provided.
- Around line 69-70: Clamp the runtime background analysis interval to a safe
minimum (e.g., 1) to avoid an immediate setTimeout re-run loop; replace direct
uses of gameCommentaryBackgroundAnalysisInterval with a small accessor like
getEffectiveBackgroundAnalysisInterval (created via useCallback) that returns
Math.max(gameCommentaryBackgroundAnalysisInterval, 1), and use that accessor
wherever the interval drives the setTimeout loop (the code around the existing
setTimeout logic that currently reads gameCommentaryBackgroundAnalysisInterval).
- Around line 291-306: scheduleNext closes over an outdated triggerCommentary
because the timer callback was created without including triggerCommentary in
deps; update scheduleNext to call a ref that always points to the latest
triggerCommentary: create triggerCommentaryRef (e.g.
triggerCommentaryRef.current) and add a useEffect that sets
triggerCommentaryRef.current = () => { void triggerCommentary() } whenever
triggerCommentary changes, then change the setTimeout and any interval callbacks
inside scheduleNext to invoke triggerCommentaryRef.current() instead of
triggerCommentary; keep clearTimers, getEffectiveCaptureInterval in deps and
remove the stale eslint-disable comment.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: dd3fbc1a-90fb-4406-a7ed-6103b2c40ecb
📒 Files selected for processing (1)
src/hooks/useGameCommentaryMode.ts
|
Claude finished @tegnike's task —— View job PR Review: Game Commentary Mode FeatureThis PR introduces a comprehensive AI-powered game commentary system that captures screen content and generates real-time commentary. Here's my detailed review: 📋 Review Summary✅ StrengthsArchitecture & Design:
Implementation Quality:
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/__tests__/components/settings/modelProvider/useAIServiceHandlers.test.ts (1)
54-56: アサーションを強化して誤検知を減らしてください。Line [54]-[56] の
toHaveBeenCalledWithだけだと、余分なsetState呼び出しがあっても通る可能性があります。toHaveBeenCalledTimes(1)の併用を推奨します。差分案
expect(mockedSettingsStore.setState).toHaveBeenCalledWith({ enableMultiModal: false, }) + expect(mockedSettingsStore.setState).toHaveBeenCalledTimes(1)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/__tests__/components/settings/modelProvider/useAIServiceHandlers.test.ts` around lines 54 - 56, 強化されたアサーションを追加して余分な呼び出しを検出できるようにしてください: 現在の期待は mockedSettingsStore.setState の引数だけを検証しているので、テスト内の該当箇所(expect(mockedSettingsStore.setState).toHaveBeenCalledWith({...}))に対して呼び出し回数のチェックを併用し、expect(mockedSettingsStore.setState).toHaveBeenCalledTimes(1) を追加して余計な setState 呼び出しがないことを保証してください。src/__tests__/hooks/useGameCommentaryMode.test.ts (1)
391-404:gameCommentaryImageQuality: 0の回帰テストも追加しておきたいです。
resizeWidth=0はカバーできていますが、同じ falsy default 系の不具合は画質設定でも起きやすいです。captureFrame(1024, 0)が呼ばれるケースも押さえておくと、この種の回帰を防ぎやすいです。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/__tests__/hooks/useGameCommentaryMode.test.ts` around lines 391 - 404, Add a regression test to cover the falsy image-quality case: in the existing test suite for useGameCommentaryMode add a test similar to the "preserves zero resize width" one but set setupSettingsState({ gameCommentaryImageQuality: 0 }) and assert that mockCaptureFrame is called with the default width (e.g. 1024) and quality 0 (expect(mockCaptureFrame).toHaveBeenCalledWith(1024, 0)); keep the same renderHook(...useGameCommentaryMode...) and timer/flush flow so the hook triggers the capture path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/hooks/useGameCommentaryMode.ts`:
- Around line 73-80: The settingsStore subscription in useGameCommentaryMode
only forces a re-render and doesn't restart any pending timers, so changes to
gameCommentaryCaptureInterval don't affect the currently waiting timeout; update
the subscription callback to clear the existing timer (use the same timeoutRef /
any existing timer id used by scheduleNext / clearTimeout) and then call
scheduleNext() so the new captureIntervalRef.current is applied immediately;
ensure you still call settingsStore.subscribe/unsubscribe as before and preserve
cleanup via the returned unsubscribe.
- Line 66: The defaulting logic for gameCommentaryImageQuality in
useGameCommentaryMode is wrong: replace the falsy OR fallback so that a
legitimate 0 value from ss.gameCommentaryImageQuality is preserved; change the
assignment that currently uses "ss.gameCommentaryImageQuality || 0.7" to use a
nullish check (or explicit undefined check) so only undefined/null falls back to
0.7, ensuring captureFrame can receive quality=0; reference variables:
gameCommentaryImageQuality, ss.gameCommentaryImageQuality, and the
useGameCommentaryMode hook, and ensure captureFrame continues to accept 0 as
valid.
---
Nitpick comments:
In
`@src/__tests__/components/settings/modelProvider/useAIServiceHandlers.test.ts`:
- Around line 54-56: 強化されたアサーションを追加して余分な呼び出しを検出できるようにしてください: 現在の期待は
mockedSettingsStore.setState
の引数だけを検証しているので、テスト内の該当箇所(expect(mockedSettingsStore.setState).toHaveBeenCalledWith({...}))に対して呼び出し回数のチェックを併用し、expect(mockedSettingsStore.setState).toHaveBeenCalledTimes(1)
を追加して余計な setState 呼び出しがないことを保証してください。
In `@src/__tests__/hooks/useGameCommentaryMode.test.ts`:
- Around line 391-404: Add a regression test to cover the falsy image-quality
case: in the existing test suite for useGameCommentaryMode add a test similar to
the "preserves zero resize width" one but set setupSettingsState({
gameCommentaryImageQuality: 0 }) and assert that mockCaptureFrame is called with
the default width (e.g. 1024) and quality 0
(expect(mockCaptureFrame).toHaveBeenCalledWith(1024, 0)); keep the same
renderHook(...useGameCommentaryMode...) and timer/flush flow so the hook
triggers the capture path.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 89537612-d4bd-4084-a0da-ad4affc52ab0
📒 Files selected for processing (5)
.github/workflows/deploy-cloudflare.ymlsrc/__tests__/components/settings/modelProvider/useAIServiceHandlers.test.tssrc/__tests__/hooks/useGameCommentaryMode.test.tssrc/components/settings/modelProvider/hooks/useAIServiceHandlers.tssrc/hooks/useGameCommentaryMode.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/components/settings/modelProvider/hooks/useAIServiceHandlers.ts
|
Claude finished @tegnike's task —— View job PR Review: Game Commentary Mode FeatureTodo List:
📋 Review SummaryThis PR introduces a comprehensive AI-powered game commentary system that captures screen content and generates real-time commentary. The implementation demonstrates solid architectural patterns and thorough consideration of edge cases. ✅ StrengthsExcellent Architecture & Design
Comprehensive Error Handling
Strong Test Coverage
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/features/chat/openAIAudioChat.ts (1)
17-17: import はパスエイリアスに統一してくださいここで追加された相対 import は、リポジトリの import ルールとズレています。エイリアス形式に揃えた方が一貫性を保てます。
差分案
-import type { AIChatResponseStreamOptions } from './aiChatFactory' +import type { AIChatResponseStreamOptions } from '@/features/chat/aiChatFactory'As per coding guidelines "
**/*.{ts,tsx}: Use path aliases:@/*→./src/*".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/features/chat/openAIAudioChat.ts` at line 17, The import in openAIAudioChat.ts uses a relative path for AIChatResponseStreamOptions; change it to the project path-alias form so imports follow the repository rule (use `@/` to map to ./src/). Update the import of AIChatResponseStreamOptions from './aiChatFactory' to the corresponding alias path (e.g., '@/features/chat/aiChatFactory') so the symbol AIChatResponseStreamOptions is imported via the path alias.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/features/chat/openAIAudioChat.ts`:
- Line 17: The import in openAIAudioChat.ts uses a relative path for
AIChatResponseStreamOptions; change it to the project path-alias form so imports
follow the repository rule (use `@/` to map to ./src/). Update the import of
AIChatResponseStreamOptions from './aiChatFactory' to the corresponding alias
path (e.g., '@/features/chat/aiChatFactory') so the symbol
AIChatResponseStreamOptions is imported via the path alias.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 8ef77c60-de68-4be5-a94d-776948ba2bd0
📒 Files selected for processing (4)
src/__tests__/components/settings/modelProvider/useAIServiceHandlers.test.tssrc/__tests__/hooks/useGameCommentaryMode.test.tssrc/features/chat/openAIAudioChat.tssrc/hooks/useGameCommentaryMode.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- src/tests/hooks/useGameCommentaryMode.test.ts
- src/tests/components/settings/modelProvider/useAIServiceHandlers.test.ts
|
Claude finished @tegnike's task —— View job PR Review: Game Commentary Mode FeatureTodo List:
📋 Review SummaryThis PR introduces a comprehensive AI-powered game commentary system that captures screen content and generates real-time commentary. The implementation demonstrates solid architectural patterns and thorough consideration of edge cases. ✅ StrengthsExcellent Architecture & Design
Comprehensive Error Handling
Strong Test Coverage
|
|
Claude finished @tegnike's task —— View job PR Review: Game Commentary Mode FeatureTodo List:
📋 Review SummaryThis PR introduces a comprehensive AI-powered game commentary system that captures screen content and generates real-time commentary. The implementation demonstrates solid architectural patterns and thorough consideration of edge cases. ✅ StrengthsExcellent Architecture & Design
Comprehensive Error Handling
Strong Test Coverage
|
There was a problem hiding this comment.
Actionable comments posted: 11
🧹 Nitpick comments (3)
locales/ar/translation.json (1)
654-654: UI文言に英語固定ラベルが残っています。Line 654 の
Captureはアラビア語UIの中で英語固定です。ボタン名称がローカライズ対象なら、翻訳側で統一したほうが一貫性が上がります。✏️ 修正例
- "NoCaptureWarning": "لم تبدأ مشاركة الشاشة. ابدأها بزر “Capture” في القائمة.", + "NoCaptureWarning": "لم تبدأ مشاركة الشاشة. ابدأها بزر «التقاط» في القائمة.",Based on learnings: Support internationalization with Japanese as the source language and delegate other language translations to a separate managed process
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@locales/ar/translation.json` at line 654, The Arabic translation for the "NoCaptureWarning" message embeds the English fixed label "Capture"; update this entry to use the localized Arabic button label (or an i18n label reference) instead of the hard-coded English word so the UI is consistent—locate the "NoCaptureWarning" key in locales/ar/translation.json and replace "Capture" with the Arabic translation of the button text or with the shared label key used elsewhere (e.g. the Capture button's translation key) so all languages render the same button name.locales/es/translation.json (1)
435-436: Line 436 のスペイン語は自然な表現に寄せる余地があります。
"Mostrar vídeo de nuevo"は意味は通りますが、UI文言としては"Volver a mostrar el vídeo"の方が自然です。✍️ 提案差分
- "ShowVideoDisplay": "Mostrar vídeo de nuevo", + "ShowVideoDisplay": "Volver a mostrar el vídeo",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@locales/es/translation.json` around lines 435 - 436, Update the Spanish translation for the key "ShowVideoDisplay" to a more natural UI phrase: replace "Mostrar vídeo de nuevo" with "Volver a mostrar el vídeo", leaving "HideVideoDisplay" unchanged.locales/ru/translation.json (1)
687-691: 削除対象として整理できます。MultiModalMode*キーは完全に未参照です。これらの翻訳キーは、廃止予定の
multiModalMode設定に対応するものですが、コード内での参照が一切ありません。src/features/stores/settings.tsでmultiModalModeからenableMultiModalへの移行処理が行われており、古い設定プロパティは削除済みです。翻訳ファイル内のこれらのキーは保守対象を増やすだけなので、削除を推奨します。🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@locales/ru/translation.json` around lines 687 - 691, Remove the dead translation keys "MultiModalMode", "MultiModalModeDescription", "MultiModalModeAIDecide", "MultiModalModeAlways", and "MultiModalModeNever" from the Russian translation file because they correspond to the deprecated multiModalMode setting (now migrated to enableMultiModal in src/features/stores/settings.ts) and are no longer referenced in code; update the locales/ru/translation.json by deleting those keys to avoid maintaining unused strings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@locales/de/translation.json`:
- Line 656: The translation for key "DisabledByExclusion" uses the inconsistent
term "Sprachmodus"; update that value so the term matches the rest of the file
by replacing "Sprachmodus" with "Audiomodus" (i.e., edit the string for
"DisabledByExclusion" to use "Audiomodus" so AudioMode terminology is consistent
across the locale).
In `@locales/es/translation.json`:
- Around line 631-665: Update the Spanish translation for the
DisabledByExclusion message to use the consistent term "Modo de audio" instead
of "Modo de voz": locate the JSON key "DisabledByExclusion" and replace the
phrase "Modo de voz" with the same "Modo de audio" string used elsewhere in the
file so the exclusion warning matches the settings names exactly.
In `@locales/fr/translation.json`:
- Around line 633-663: The French locale uses the Japanese range marker "〜"
inconsistently; replace it with the natural French "à" across the affected keys
(e.g., "CaptureIntervalInfo", "ContextCountInfo", "ImageQualityInfo",
"BackgroundAnalysisIntervalInfo", "VideoDelayInfo") and adjust the wording of
"CaptureIntervalInfo" to a more natural French word order (e.g., "Définissez
l’intervalle de capture de l’écran : {{min}} à {{max}} secondes."). Ensure all
other instances of "({{min}}〜{{max}})" follow the same pattern and keep the
existing keys (EnableInfo, CaptureInterval, CaptureIntervalInfo, ContextCount,
ContextCountInfo, PromptTemplateInfo, ImageQualityInfo, ResizeWidthInfo,
BackgroundAnalysisIntervalInfo, VideoDelayInfo) unchanged except for the textual
fixes.
In `@locales/hi/translation.json`:
- Around line 96-97: The hi locale file contains manual additions ("CopyEnvVars"
and "CopyEnvVarsInfo") that violate the policy of only updating Japanese locale
files; revert these manual changes from the hi translation (remove the
"CopyEnvVars" and "CopyEnvVarsInfo" entries) and instead add the new keys and
strings to the ja locale source so translations can be propagated by the proper
workflow; repeat this revert for any other non-ja files where the same new keys
were added.
In `@locales/it/translation.json`:
- Around line 687-691: Remove the unused translation keys MultiModalMode,
MultiModalModeDescription, MultiModalModeAIDecide, MultiModalModeAlways, and
MultiModalModeNever from this Italian translation file (and the other language
files) because the codebase only uses EnableMultiModal and
EnableMultiModalDescription; search for those specific keys (MultiModalMode*,
EnableMultiModal, EnableMultiModalDescription) to confirm usage, then delete the
unused MultiModalMode* entries across all locales to reduce maintenance overhead
and keep translations in sync.
In `@locales/ko/translation.json`:
- Around line 96-97: Revert the manual Korean translations you added (e.g. the
keys "CopyEnvVars" and "CopyEnvVarsInfo" and the other newly added keys in the
same commit) so that only the Japanese locale receives the new strings; undo the
edits in the Korean translation JSON and leave the new keys present only under
the Japanese locale, allowing the translation synchronization workflow to
propagate translations for ko instead of committing manual ko changes.
In `@locales/pl/translation.json`:
- Line 654: The string value for the translation key "NoCaptureWarning" contains
the English button label "Capture"; update this value to use the Polish UI
wording instead (e.g., replace "Capture" with the Polish equivalent used in the
app like "Przechwyć" or the actual Polish button text) so the message no longer
depends on an English label and matches the rest of the Polish UI.
In `@locales/pt/translation.json`:
- Around line 684-691: Add the missing translation keys MultiModalMode,
MultiModalModeDescription, MultiModalModeAIDecide (and also MultiModalModeAlways
and MultiModalModeNever) to the Japanese translation file with appropriate
Japanese strings, ensuring the keys match exactly the ones added in the
Portuguese file; after committing those additions to the Japanese file only, run
the project's translation update tool/process to propagate the new keys to other
language files automatically.
In `@locales/ru/translation.json`:
- Around line 654-656: Update the Russian strings to use the localized UI label
instead of hard-coded English and to use consistent terminology: in the
"NoCaptureWarning" value replace the literal “Capture” with the localized label
placeholder (e.g., the same label used in the UI for the screen-share/capture
action) so it reads with the localized button name rather than the English word,
and in "DisabledByExclusion" replace "голосовой режим" with the existing project
term "Аудиорежим" to keep naming consistent with other keys.
In `@locales/th/translation.json`:
- Around line 684-691: Revert the manual edits in locales/th/translation.json
and restore it to match the canonical translations managed by the translation
pipeline; specifically remove the unintended placeholder {{count}} from the Thai
"ConversationHistoryInfo" entry so it matches the Japanese source, and delete
the five added keys ("MultiModalMode", "MultiModalModeDescription",
"MultiModalModeAIDecide", "MultiModalModeAlways", "MultiModalModeNever") that
are not present in the Japanese source; instead, make any required updates in
the Japanese files under locales/ja/ and trigger the established localization
process so other languages are updated consistently.
In `@locales/zh-TW/translation.json`:
- Around line 631-665: The translation uses inconsistent terminology: the
GameCommentary.DisabledByExclusion value contains "外部連動" but the file already
uses "外部連接模式" elsewhere; update the DisabledByExclusion string to use the
existing term "外部連接模式" so wording is consistent across the locale (search for
the "GameCommentary.DisabledByExclusion" key and replace "外部連動" with "外部連接模式").
---
Nitpick comments:
In `@locales/ar/translation.json`:
- Line 654: The Arabic translation for the "NoCaptureWarning" message embeds the
English fixed label "Capture"; update this entry to use the localized Arabic
button label (or an i18n label reference) instead of the hard-coded English word
so the UI is consistent—locate the "NoCaptureWarning" key in
locales/ar/translation.json and replace "Capture" with the Arabic translation of
the button text or with the shared label key used elsewhere (e.g. the Capture
button's translation key) so all languages render the same button name.
In `@locales/es/translation.json`:
- Around line 435-436: Update the Spanish translation for the key
"ShowVideoDisplay" to a more natural UI phrase: replace "Mostrar vídeo de nuevo"
with "Volver a mostrar el vídeo", leaving "HideVideoDisplay" unchanged.
In `@locales/ru/translation.json`:
- Around line 687-691: Remove the dead translation keys "MultiModalMode",
"MultiModalModeDescription", "MultiModalModeAIDecide", "MultiModalModeAlways",
and "MultiModalModeNever" from the Russian translation file because they
correspond to the deprecated multiModalMode setting (now migrated to
enableMultiModal in src/features/stores/settings.ts) and are no longer
referenced in code; update the locales/ru/translation.json by deleting those
keys to avoid maintaining unused strings.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 6cba2886-ebd4-48cb-8304-5a95a349b386
📒 Files selected for processing (16)
.gitignorelocales/ar/translation.jsonlocales/de/translation.jsonlocales/en/translation.jsonlocales/es/translation.jsonlocales/fr/translation.jsonlocales/hi/translation.jsonlocales/it/translation.jsonlocales/ko/translation.jsonlocales/pl/translation.jsonlocales/pt/translation.jsonlocales/ru/translation.jsonlocales/th/translation.jsonlocales/vi/translation.jsonlocales/zh-CN/translation.jsonlocales/zh-TW/translation.json
✅ Files skipped from review due to trivial changes (1)
- .gitignore
🚧 Files skipped from review as they are similar to previous changes (1)
- locales/en/translation.json
| "BackgroundAnalysisIntervalInfo": "Legt fest, wie oft die zusätzliche Bildanalyse während der Sprachausgabe ausgeführt wird ({{min}}〜{{max}} Sekunden). Kürzere Intervalle folgen der Szene besser, erhöhen aber die API-Kosten.", | ||
| "NoCaptureWarning": "Die Bildschirmfreigabe wurde nicht gestartet. Starten Sie sie über die Schaltfläche „Capture“ im Menü.", | ||
| "NonMultiModalWarning": "Das aktuell ausgewählte KI-Modell unterstützt keine multimodale Eingabe (Bilder). Der Spielkommentar-Modus benötigt ein multimodales Modell wie Gemini oder GPT-4o.", | ||
| "DisabledByExclusion": "Der Spielkommentar-Modus kann nicht verwendet werden, da ein anderer Modus aktiv ist (Realtime API / Sprachmodus / Externe Verknüpfung / Idle-Modus).", |
There was a problem hiding this comment.
用語の一貫性: Sprachmodus は Audiomodus に揃えるのを推奨します。
同一ファイル内で AudioMode 系は「Audiomodus」を使っており、ここだけ表記が異なるため、設定の排他理由がやや分かりにくくなります。
修正案
- "DisabledByExclusion": "Der Spielkommentar-Modus kann nicht verwendet werden, da ein anderer Modus aktiv ist (Realtime API / Sprachmodus / Externe Verknüpfung / Idle-Modus).",
+ "DisabledByExclusion": "Der Spielkommentar-Modus kann nicht verwendet werden, da ein anderer Modus aktiv ist (Realtime API / Audiomodus / Externe Verknüpfung / Idle-Modus).",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "DisabledByExclusion": "Der Spielkommentar-Modus kann nicht verwendet werden, da ein anderer Modus aktiv ist (Realtime API / Sprachmodus / Externe Verknüpfung / Idle-Modus).", | |
| "DisabledByExclusion": "Der Spielkommentar-Modus kann nicht verwendet werden, da ein anderer Modus aktiv ist (Realtime API / Audiomodus / Externe Verknüpfung / Idle-Modus).", |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@locales/de/translation.json` at line 656, The translation for key
"DisabledByExclusion" uses the inconsistent term "Sprachmodus"; update that
value so the term matches the rest of the file by replacing "Sprachmodus" with
"Audiomodus" (i.e., edit the string for "DisabledByExclusion" to use
"Audiomodus" so AudioMode terminology is consistent across the locale).
| "GameCommentarySettings": "Configuración de comentarios de juego", | ||
| "GameCommentary": { | ||
| "Enable": "Modo de comentarios de juego", | ||
| "EnableInfo": "Captura la pantalla a intervalos regulares y la IA genera y pronuncia comentarios en tiempo real. Se requiere un modelo de IA multimodal.", | ||
| "CaptureInterval": "Intervalo de captura (segundos)", | ||
| "CaptureIntervalInfo": "Define el intervalo de captura de pantalla ({{min}}〜{{max}} segundos).", | ||
| "ContextCount": "Número de comentarios previos de referencia", | ||
| "ContextCountInfo": "Define cuántos comentarios anteriores se envían a la IA ({{min}}〜{{max}}). Cuantos más haya, más contexto se considera, pero aumenta el coste de API.", | ||
| "PromptTemplate": "Prompt de comentarios", | ||
| "PromptTemplateInfo": "Prompt del sistema usado cuando la IA comenta el juego. Se combina con el prompt del sistema de la configuración del personaje.\nSi incluyes la etiqueta [scene] en el formato de salida, la IA generará una descripción de la escena y la usará como contexto de pantalla anterior en comentarios posteriores.", | ||
| "ImageQuality": "Calidad de imagen", | ||
| "ImageQualityInfo": "Calidad JPEG de la imagen capturada ({{min}}〜{{max}}). Cuanto menor sea, menor será el tamaño de datos y el coste de API.", | ||
| "ResizeWidth": "Ancho de redimensionado de imagen", | ||
| "ResizeWidthInfo": "Ancho de redimensionado de la captura en píxeles. Valores menores reducen el tamaño de datos.", | ||
| "ResizeWidthNone": "Sin redimensionar", | ||
| "SaveToChat": "Guardar comentarios en el historial de chat", | ||
| "SaveToChatInfo": "Si se activa, los comentarios también aparecerán en el registro de chat. Si se desactiva, solo se conservarán en el búfer dedicado.", | ||
| "BackgroundAnalysis": "Análisis auxiliar de imagen durante el habla", | ||
| "BackgroundAnalysisInfo": "Si se activa, la pantalla se analiza con baja frecuencia mientras se reproduce el comentario y se pasa como información auxiliar para el siguiente comentario. Refuerza el flujo de la escena, pero genera costes adicionales de API multimodal. Se recomienda solo con modelos rápidos de análisis de imagen.", | ||
| "BackgroundAnalysisPromptTemplate": "Prompt de análisis auxiliar de imagen", | ||
| "BackgroundAnalysisPromptTemplateInfo": "Prompt del sistema para el análisis auxiliar de imagen durante el habla. Aquí puedes especificar el género del juego y los elementos a observar. Incluir información para identificar el juego mejora la estabilidad.", | ||
| "BackgroundAnalysisInterval": "Intervalo de análisis auxiliar de imagen (segundos)", | ||
| "BackgroundAnalysisIntervalInfo": "Define cada cuántos segundos se ejecuta el análisis auxiliar durante el habla ({{min}}〜{{max}} segundos). Intervalos más cortos siguen mejor la escena, pero aumentan el coste de API.", | ||
| "NoCaptureWarning": "La pantalla compartida no se ha iniciado. Iníciala con el botón “Capture” del menú.", | ||
| "NonMultiModalWarning": "El modelo de IA seleccionado no admite multimodalidad (entrada de imágenes). El modo de comentarios de juego requiere un modelo multimodal como Gemini o GPT-4o.", | ||
| "DisabledByExclusion": "No se puede usar el modo de comentarios de juego porque otro modo está activo (Realtime API / Modo de voz / Enlace externo / Modo inactivo).", | ||
| "ApiCostWarning": "⚠️ El modo de comentarios de juego llama a una API multimodal en cada intervalo de captura, por lo que se generarán costes de API. Ten cuidado.", | ||
| "Speaking": "Comentando", | ||
| "Capturing": "Capturando", | ||
| "WaitingPrefix": "Espera", | ||
| "CaptureUnavailable": "Captura detenida", | ||
| "VideoDelay": "Retraso de vídeo (segundos)", | ||
| "VideoDelayInfo": "Retrasa la visualización del vídeo compartido el número de segundos indicado ({{min}}〜{{max}} segundos). Compensa el tiempo de generación de comentarios por IA y alinea la voz con el vídeo. 0 lo desactiva. Las capturas enviadas a la IA siguen siendo en tiempo real.", | ||
| "VideoDelayOff": "OFF" | ||
| }, |
There was a problem hiding this comment.
排他モード警告の用語を統一してください。
Line 656 の "Modo de voz" は、同ファイル内で使われている "Modo de audio" と不一致です。設定名とのズレでユーザーが混乱しやすいため、用語統一を推奨します。
🛠️ 提案差分
- "DisabledByExclusion": "No se puede usar el modo de comentarios de juego porque otro modo está activo (Realtime API / Modo de voz / Enlace externo / Modo inactivo).",
+ "DisabledByExclusion": "No se puede usar el modo de comentarios de juego porque otro modo está activo (Realtime API / Modo de audio / Enlace externo / Modo inactivo).",Based on learnings: Maintain mutual exclusivity of incompatible settings through the exclusion control middleware system to prevent configuration conflicts.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@locales/es/translation.json` around lines 631 - 665, Update the Spanish
translation for the DisabledByExclusion message to use the consistent term "Modo
de audio" instead of "Modo de voz": locate the JSON key "DisabledByExclusion"
and replace the phrase "Modo de voz" with the same "Modo de audio" string used
elsewhere in the file so the exclusion warning matches the settings names
exactly.
| "Enable": "Mode commentaire de jeu", | ||
| "EnableInfo": "Capture l’écran à intervalles réguliers, puis l’IA génère et prononce un commentaire en temps réel. Un modèle d’IA multimodal est requis.", | ||
| "CaptureInterval": "Intervalle de capture (secondes)", | ||
| "CaptureIntervalInfo": "Définissez l’intervalle de capture de l’écran ({{min}}〜{{max}} secondes).", | ||
| "ContextCount": "Nombre d’historiques de commentaire à référencer", | ||
| "ContextCountInfo": "Définissez le nombre de commentaires précédents envoyés à l’IA ({{min}}〜{{max}}). Plus ce nombre est élevé, plus le contexte est pris en compte, mais les coûts d’API augmentent.", | ||
| "PromptTemplate": "Prompt de commentaire", | ||
| "PromptTemplateInfo": "Prompt système utilisé lorsque l’IA commente le jeu. Il est combiné avec le prompt système des paramètres du personnage.\nSi vous incluez la balise [scene] dans le format de sortie, l’IA génère une description de la scène et l’utilise comme contexte d’écran précédent pour les commentaires suivants.", | ||
| "ImageQuality": "Qualité d’image", | ||
| "ImageQualityInfo": "Qualité JPEG de l’image capturée ({{min}}〜{{max}}). Une valeur plus basse réduit la taille des données et les coûts d’API.", | ||
| "ResizeWidth": "Largeur de redimensionnement de l’image", | ||
| "ResizeWidthInfo": "Largeur de redimensionnement des captures en pixels. Une valeur plus petite réduit la taille des données.", | ||
| "ResizeWidthNone": "Pas de redimensionnement", | ||
| "SaveToChat": "Enregistrer les commentaires dans l’historique du chat", | ||
| "SaveToChatInfo": "Si activé, les commentaires apparaissent aussi dans le journal de chat. Sinon, ils sont conservés uniquement dans le tampon dédié.", | ||
| "BackgroundAnalysis": "Analyse d’image auxiliaire pendant la parole", | ||
| "BackgroundAnalysisInfo": "Si activé, l’écran est aussi analysé à faible fréquence pendant la lecture du commentaire et transmis comme information auxiliaire pour le commentaire suivant. Cela améliore la continuité de la scène, mais entraîne des coûts supplémentaires d’API multimodale. Recommandé uniquement avec un modèle d’analyse d’image rapide.", | ||
| "BackgroundAnalysisPromptTemplate": "Prompt d’analyse d’image auxiliaire", | ||
| "BackgroundAnalysisPromptTemplateInfo": "Prompt système utilisé pour l’analyse d’image auxiliaire pendant la parole. Vous pouvez y préciser le genre du jeu et les éléments à observer. Ajouter des informations permettant d’identifier le jeu améliore la stabilité.", | ||
| "BackgroundAnalysisInterval": "Intervalle d’analyse d’image auxiliaire (secondes)", | ||
| "BackgroundAnalysisIntervalInfo": "Définissez la fréquence de l’analyse d’image auxiliaire pendant la parole ({{min}}〜{{max}} secondes). Un intervalle plus court suit mieux la scène, mais augmente les coûts d’API.", | ||
| "NoCaptureWarning": "Le partage d’écran n’a pas commencé. Lancez-le avec le bouton « Capture » du menu.", | ||
| "NonMultiModalWarning": "Le modèle d’IA sélectionné ne prend pas en charge le multimodal (entrée image). Le mode commentaire de jeu nécessite un modèle multimodal comme Gemini ou GPT-4o.", | ||
| "DisabledByExclusion": "Le mode commentaire de jeu ne peut pas être utilisé car un autre mode est activé (Realtime API / Mode vocal / Liaison externe / Mode inactif).", | ||
| "ApiCostWarning": "⚠️ Le mode commentaire de jeu appelle une API multimodale à chaque intervalle de capture, ce qui entraîne des coûts d’API. Soyez prudent.", | ||
| "Speaking": "Commentaire en cours", | ||
| "Capturing": "Capture en cours", | ||
| "WaitingPrefix": "Attente", | ||
| "CaptureUnavailable": "Capture arrêtée", | ||
| "VideoDelay": "Délai vidéo (secondes)", | ||
| "VideoDelayInfo": "Retarde l’affichage vidéo du partage d’écran du nombre de secondes indiqué ({{min}}〜{{max}} secondes). Cela compense le temps de génération du commentaire par l’IA et synchronise la voix avec la vidéo. 0 désactive l’option. Les captures envoyées à l’IA restent en temps réel.", |
There was a problem hiding this comment.
フランス語UI文言で表記ゆれ(「〜」)があります。
フランス語ロケールでは範囲表記を à に統一した方が自然です。あわせて Line 633 の語順も自然化できます。
✏️ 文言修正案
- "Enable": "Mode commentaire de jeu",
+ "Enable": "Mode de commentaire de jeu",
- "CaptureIntervalInfo": "Définissez l’intervalle de capture de l’écran ({{min}}〜{{max}} secondes).",
+ "CaptureIntervalInfo": "Définissez l’intervalle de capture de l’écran ({{min}} à {{max}} secondes).",
- "ContextCountInfo": "Définissez le nombre de commentaires précédents envoyés à l’IA ({{min}}〜{{max}}). Plus ce nombre est élevé, plus le contexte est pris en compte, mais les coûts d’API augmentent.",
+ "ContextCountInfo": "Définissez le nombre de commentaires précédents envoyés à l’IA ({{min}} à {{max}}). Plus ce nombre est élevé, plus le contexte est pris en compte, mais les coûts d’API augmentent.",
- "ImageQualityInfo": "Qualité JPEG de l’image capturée ({{min}}〜{{max}}). Une valeur plus basse réduit la taille des données et les coûts d’API.",
+ "ImageQualityInfo": "Qualité JPEG de l’image capturée ({{min}} à {{max}}). Une valeur plus basse réduit la taille des données et les coûts d’API.",
- "BackgroundAnalysisIntervalInfo": "Définissez la fréquence de l’analyse d’image auxiliaire pendant la parole ({{min}}〜{{max}} secondes). Un intervalle plus court suit mieux la scène, mais augmente les coûts d’API.",
+ "BackgroundAnalysisIntervalInfo": "Définissez la fréquence de l’analyse d’image auxiliaire pendant la parole ({{min}} à {{max}} secondes). Un intervalle plus court suit mieux la scène, mais augmente les coûts d’API.",
- "VideoDelayInfo": "Retarde l’affichage vidéo du partage d’écran du nombre de secondes indiqué ({{min}}〜{{max}} secondes). Cela compense le temps de génération du commentaire par l’IA et synchronise la voix avec la vidéo. 0 désactive l’option. Les captures envoyées à l’IA restent en temps réel.",
+ "VideoDelayInfo": "Retarde l’affichage vidéo du partage d’écran du nombre de secondes indiqué ({{min}} à {{max}} secondes). Cela compense le temps de génération du commentaire par l’IA et synchronise la voix avec la vidéo. 0 désactive l’option. Les captures envoyées à l’IA restent en temps réel.",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "Enable": "Mode commentaire de jeu", | |
| "EnableInfo": "Capture l’écran à intervalles réguliers, puis l’IA génère et prononce un commentaire en temps réel. Un modèle d’IA multimodal est requis.", | |
| "CaptureInterval": "Intervalle de capture (secondes)", | |
| "CaptureIntervalInfo": "Définissez l’intervalle de capture de l’écran ({{min}}〜{{max}} secondes).", | |
| "ContextCount": "Nombre d’historiques de commentaire à référencer", | |
| "ContextCountInfo": "Définissez le nombre de commentaires précédents envoyés à l’IA ({{min}}〜{{max}}). Plus ce nombre est élevé, plus le contexte est pris en compte, mais les coûts d’API augmentent.", | |
| "PromptTemplate": "Prompt de commentaire", | |
| "PromptTemplateInfo": "Prompt système utilisé lorsque l’IA commente le jeu. Il est combiné avec le prompt système des paramètres du personnage.\nSi vous incluez la balise [scene] dans le format de sortie, l’IA génère une description de la scène et l’utilise comme contexte d’écran précédent pour les commentaires suivants.", | |
| "ImageQuality": "Qualité d’image", | |
| "ImageQualityInfo": "Qualité JPEG de l’image capturée ({{min}}〜{{max}}). Une valeur plus basse réduit la taille des données et les coûts d’API.", | |
| "ResizeWidth": "Largeur de redimensionnement de l’image", | |
| "ResizeWidthInfo": "Largeur de redimensionnement des captures en pixels. Une valeur plus petite réduit la taille des données.", | |
| "ResizeWidthNone": "Pas de redimensionnement", | |
| "SaveToChat": "Enregistrer les commentaires dans l’historique du chat", | |
| "SaveToChatInfo": "Si activé, les commentaires apparaissent aussi dans le journal de chat. Sinon, ils sont conservés uniquement dans le tampon dédié.", | |
| "BackgroundAnalysis": "Analyse d’image auxiliaire pendant la parole", | |
| "BackgroundAnalysisInfo": "Si activé, l’écran est aussi analysé à faible fréquence pendant la lecture du commentaire et transmis comme information auxiliaire pour le commentaire suivant. Cela améliore la continuité de la scène, mais entraîne des coûts supplémentaires d’API multimodale. Recommandé uniquement avec un modèle d’analyse d’image rapide.", | |
| "BackgroundAnalysisPromptTemplate": "Prompt d’analyse d’image auxiliaire", | |
| "BackgroundAnalysisPromptTemplateInfo": "Prompt système utilisé pour l’analyse d’image auxiliaire pendant la parole. Vous pouvez y préciser le genre du jeu et les éléments à observer. Ajouter des informations permettant d’identifier le jeu améliore la stabilité.", | |
| "BackgroundAnalysisInterval": "Intervalle d’analyse d’image auxiliaire (secondes)", | |
| "BackgroundAnalysisIntervalInfo": "Définissez la fréquence de l’analyse d’image auxiliaire pendant la parole ({{min}}〜{{max}} secondes). Un intervalle plus court suit mieux la scène, mais augmente les coûts d’API.", | |
| "NoCaptureWarning": "Le partage d’écran n’a pas commencé. Lancez-le avec le bouton « Capture » du menu.", | |
| "NonMultiModalWarning": "Le modèle d’IA sélectionné ne prend pas en charge le multimodal (entrée image). Le mode commentaire de jeu nécessite un modèle multimodal comme Gemini ou GPT-4o.", | |
| "DisabledByExclusion": "Le mode commentaire de jeu ne peut pas être utilisé car un autre mode est activé (Realtime API / Mode vocal / Liaison externe / Mode inactif).", | |
| "ApiCostWarning": "⚠️ Le mode commentaire de jeu appelle une API multimodale à chaque intervalle de capture, ce qui entraîne des coûts d’API. Soyez prudent.", | |
| "Speaking": "Commentaire en cours", | |
| "Capturing": "Capture en cours", | |
| "WaitingPrefix": "Attente", | |
| "CaptureUnavailable": "Capture arrêtée", | |
| "VideoDelay": "Délai vidéo (secondes)", | |
| "VideoDelayInfo": "Retarde l’affichage vidéo du partage d’écran du nombre de secondes indiqué ({{min}}〜{{max}} secondes). Cela compense le temps de génération du commentaire par l’IA et synchronise la voix avec la vidéo. 0 désactive l’option. Les captures envoyées à l’IA restent en temps réel.", | |
| "Enable": "Mode de commentaire de jeu", | |
| "EnableInfo": "Capture l'écran à intervalles réguliers, puis l'IA génère et prononce un commentaire en temps réel. Un modèle d'IA multimodal est requis.", | |
| "CaptureInterval": "Intervalle de capture (secondes)", | |
| "CaptureIntervalInfo": "Définissez l'intervalle de capture de l'écran ({{min}} à {{max}} secondes).", | |
| "ContextCount": "Nombre d'historiques de commentaire à référencer", | |
| "ContextCountInfo": "Définissez le nombre de commentaires précédents envoyés à l'IA ({{min}} à {{max}}). Plus ce nombre est élevé, plus le contexte est pris en compte, mais les coûts d'API augmentent.", | |
| "PromptTemplate": "Prompt de commentaire", | |
| "PromptTemplateInfo": "Prompt système utilisé lorsque l'IA commente le jeu. Il est combiné avec le prompt système des paramètres du personnage.\nSi vous incluez la balise [scene] dans le format de sortie, l'IA génère une description de la scène et l'utilise comme contexte d'écran précédent pour les commentaires suivants.", | |
| "ImageQuality": "Qualité d'image", | |
| "ImageQualityInfo": "Qualité JPEG de l'image capturée ({{min}} à {{max}}). Une valeur plus basse réduit la taille des données et les coûts d'API.", | |
| "ResizeWidth": "Largeur de redimensionnement de l'image", | |
| "ResizeWidthInfo": "Largeur de redimensionnement des captures en pixels. Une valeur plus petite réduit la taille des données.", | |
| "ResizeWidthNone": "Pas de redimensionnement", | |
| "SaveToChat": "Enregistrer les commentaires dans l'historique du chat", | |
| "SaveToChatInfo": "Si activé, les commentaires apparaissent aussi dans le journal de chat. Sinon, ils sont conservés uniquement dans le tampon dédié.", | |
| "BackgroundAnalysis": "Analyse d'image auxiliaire pendant la parole", | |
| "BackgroundAnalysisInfo": "Si activé, l'écran est aussi analysé à faible fréquence pendant la lecture du commentaire et transmis comme information auxiliaire pour le commentaire suivant. Cela améliore la continuité de la scène, mais entraîne des coûts supplémentaires d'API multimodale. Recommandé uniquement avec un modèle d'analyse d'image rapide.", | |
| "BackgroundAnalysisPromptTemplate": "Prompt d'analyse d'image auxiliaire", | |
| "BackgroundAnalysisPromptTemplateInfo": "Prompt système utilisé pour l'analyse d'image auxiliaire pendant la parole. Vous pouvez y préciser le genre du jeu et les éléments à observer. Ajouter des informations permettant d'identifier le jeu améliore la stabilité.", | |
| "BackgroundAnalysisInterval": "Intervalle d'analyse d'image auxiliaire (secondes)", | |
| "BackgroundAnalysisIntervalInfo": "Définissez la fréquence de l'analyse d'image auxiliaire pendant la parole ({{min}} à {{max}} secondes). Un intervalle plus court suit mieux la scène, mais augmente les coûts d'API.", | |
| "NoCaptureWarning": "Le partage d'écran n'a pas commencé. Lancez-le avec le bouton « Capture » du menu.", | |
| "NonMultiModalWarning": "Le modèle d'IA sélectionné ne prend pas en charge le multimodal (entrée image). Le mode commentaire de jeu nécessite un modèle multimodal comme Gemini ou GPT-4o.", | |
| "DisabledByExclusion": "Le mode commentaire de jeu ne peut pas être utilisé car un autre mode est activé (Realtime API / Mode vocal / Liaison externe / Mode inactif).", | |
| "ApiCostWarning": "⚠️ Le mode commentaire de jeu appelle une API multimodale à chaque intervalle de capture, ce qui entraîne des coûts d'API. Soyez prudent.", | |
| "Speaking": "Commentaire en cours", | |
| "Capturing": "Capture en cours", | |
| "WaitingPrefix": "Attente", | |
| "CaptureUnavailable": "Capture arrêtée", | |
| "VideoDelay": "Délai vidéo (secondes)", | |
| "VideoDelayInfo": "Retarde l'affichage vidéo du partage d'écran du nombre de secondes indiqué ({{min}} à {{max}} secondes). Cela compense le temps de génération du commentaire par l'IA et synchronise la voix avec la vidéo. 0 désactive l'option. Les captures envoyées à l'IA restent en temps réel.", |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@locales/fr/translation.json` around lines 633 - 663, The French locale uses
the Japanese range marker "〜" inconsistently; replace it with the natural French
"à" across the affected keys (e.g., "CaptureIntervalInfo", "ContextCountInfo",
"ImageQualityInfo", "BackgroundAnalysisIntervalInfo", "VideoDelayInfo") and
adjust the wording of "CaptureIntervalInfo" to a more natural French word order
(e.g., "Définissez l’intervalle de capture de l’écran : {{min}} à {{max}}
secondes."). Ensure all other instances of "({{min}}〜{{max}})" follow the same
pattern and keep the existing keys (EnableInfo, CaptureInterval,
CaptureIntervalInfo, ContextCount, ContextCountInfo, PromptTemplateInfo,
ImageQualityInfo, ResizeWidthInfo, BackgroundAnalysisIntervalInfo,
VideoDelayInfo) unchanged except for the textual fixes.
| "CopyEnvVars": "पर्यावरण चर कॉपी करें", | ||
| "CopyEnvVarsInfo": "वर्तमान कैरेक्टर स्थिति को पर्यावरण चर प्रारूप में क्लिपबोर्ड पर कॉपी करता है। आप इसे .env फ़ाइल में पेस्ट कर सकते हैं।", |
There was a problem hiding this comment.
非日本語ロケールの手動更新は避けてください
このリポジトリの運用方針では、新規翻訳キー追加時は /locales/ja/ のみを更新し、他言語は別の管理プロセスで反映する前提です。locales/hi/translation.json への今回の手動追加はその方針と競合するため、将来の同期ズレや上書きロストの原因になります。ja 側への反映に寄せ、このファイルの手動差分は戻すのが安全です。
Based on learnings: Applies to locales/ja/**/*.json : Update only Japanese language files (/locales/ja/) when adding new translation strings; do not manually update other language files
Also applies to: 308-308, 435-436, 524-524, 631-665, 684-691
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@locales/hi/translation.json` around lines 96 - 97, The hi locale file
contains manual additions ("CopyEnvVars" and "CopyEnvVarsInfo") that violate the
policy of only updating Japanese locale files; revert these manual changes from
the hi translation (remove the "CopyEnvVars" and "CopyEnvVarsInfo" entries) and
instead add the new keys and strings to the ja locale source so translations can
be propagated by the proper workflow; repeat this revert for any other non-ja
files where the same new keys were added.
| "BackgroundAnalysisPromptTemplateInfo": "Prompt systemowy używany do pomocniczej analizy obrazu podczas mówienia. Możesz tu wskazać gatunek gry i elementy do obserwowania. Informacje identyfikujące grę poprawiają stabilność.", | ||
| "BackgroundAnalysisInterval": "Interwał pomocniczej analizy obrazu (sekundy)", | ||
| "BackgroundAnalysisIntervalInfo": "Ustaw, co ile sekund wykonywać pomocniczą analizę obrazu podczas mówienia ({{min}}〜{{max}} s). Krótszy interwał lepiej śledzi scenę, ale zwiększa koszt API.", | ||
| "NoCaptureWarning": "Udostępnianie ekranu nie zostało rozpoczęte. Uruchom je przyciskiem „Capture” w menu.", |
There was a problem hiding this comment.
NoCaptureWarning に英語ラベルが残っています
Line 654 の Capture はポーランド語UI内で浮きやすいため、英語ボタン名依存を避けた表現に統一してください。
✏️ 修正案
- "NoCaptureWarning": "Udostępnianie ekranu nie zostało rozpoczęte. Uruchom je przyciskiem „Capture” w menu.",
+ "NoCaptureWarning": "Udostępnianie ekranu nie zostało rozpoczęte. Uruchom je przyciskiem przechwytywania w menu.",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "NoCaptureWarning": "Udostępnianie ekranu nie zostało rozpoczęte. Uruchom je przyciskiem „Capture” w menu.", | |
| "NoCaptureWarning": "Udostępnianie ekranu nie zostało rozpoczęte. Uruchom je przyciskiem przechwytywania w menu.", |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@locales/pl/translation.json` at line 654, The string value for the
translation key "NoCaptureWarning" contains the English button label "Capture";
update this value to use the Polish UI wording instead (e.g., replace "Capture"
with the Polish equivalent used in the app like "Przechwyć" or the actual Polish
button text) so the message no longer depends on an English label and matches
the rest of the Polish UI.
| "NoCaptureWarning": "Демонстрация экрана не начата. Запустите ее кнопкой “Capture” в меню.", | ||
| "NonMultiModalWarning": "Выбранная модель ИИ не поддерживает мультимодальность (ввод изображений). Для режима игрового комментирования нужна мультимодальная модель, например Gemini или GPT-4o.", | ||
| "DisabledByExclusion": "Режим игрового комментирования недоступен, потому что включен другой режим (Realtime API / голосовой режим / внешняя интеграция / режим ожидания).", |
There was a problem hiding this comment.
操作ラベルの英語固定と用語ゆれを修正してください。
Line 654 の Capture が英語固定で、ローカライズ済み UI ラベルと不一致になる可能性があります。
Line 656 の голосовой режим は既存キーの Аудиорежим と用語がずれており、設定名の一貫性が落ちます。
✏️ 修正例
- "NoCaptureWarning": "Демонстрация экрана не начата. Запустите ее кнопкой “Capture” в меню.",
+ "NoCaptureWarning": "Демонстрация экрана не начата. Запустите её кнопкой захвата в меню.",
- "DisabledByExclusion": "Режим игрового комментирования недоступен, потому что включен другой режим (Realtime API / голосовой режим / внешняя интеграция / режим ожидания).",
+ "DisabledByExclusion": "Режим игрового комментирования недоступен, потому что включён другой режим (Realtime API / аудиорежим / внешняя интеграция / режим ожидания).",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "NoCaptureWarning": "Демонстрация экрана не начата. Запустите ее кнопкой “Capture” в меню.", | |
| "NonMultiModalWarning": "Выбранная модель ИИ не поддерживает мультимодальность (ввод изображений). Для режима игрового комментирования нужна мультимодальная модель, например Gemini или GPT-4o.", | |
| "DisabledByExclusion": "Режим игрового комментирования недоступен, потому что включен другой режим (Realtime API / голосовой режим / внешняя интеграция / режим ожидания).", | |
| "NoCaptureWarning": "Демонстрация экрана не начата. Запустите её кнопкой захвата в меню.", | |
| "NonMultiModalWarning": "Выбранная модель ИИ не поддерживает мультимодальность (ввод изображений). Для режима игрового комментирования нужна мультимодальная модель, например Gemini или GPT-4o.", | |
| "DisabledByExclusion": "Режим игрового комментирования недоступен, потому что включён другой режим (Realtime API / аудиорежим / внешняя интеграция / режим ожидания).", |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@locales/ru/translation.json` around lines 654 - 656, Update the Russian
strings to use the localized UI label instead of hard-coded English and to use
consistent terminology: in the "NoCaptureWarning" value replace the literal
“Capture” with the localized label placeholder (e.g., the same label used in the
UI for the screen-share/capture action) so it reads with the localized button
name rather than the English word, and in "DisabledByExclusion" replace
"голосовой режим" with the existing project term "Аудиорежим" to keep naming
consistent with other keys.
| "DemoModeAppNotice": "นี่คือแอปเดโมของ AITuberKit", | ||
| "DemoModeLimitedFeaturesNotice": "ฟีเจอร์บางส่วนถูกจำกัดในเวอร์ชันเดโม", | ||
| "DemoModeLogNotice": "แอปนี้ถูกตั้งค่าให้บันทึกบันทึกการสนทนาไว้บนเซิร์ฟเวอร์ บันทึกอาจถูกประมวลผลบางส่วนแล้วเผยแพร่ได้", | ||
| "MultiModalMode": "โหมดการใช้งานมัลติโหมด", | ||
| "MultiModalModeDescription": "โปรดเลือกเมื่อใดที่จะใช้ฟังก์ชันมัลติโมดอล", | ||
| "MultiModalModeAIDecide": "ตัดสินใจด้วย AI", | ||
| "MultiModalModeAlways": "ใช้เสมอ", | ||
| "MultiModalModeNever": "ไม่ใช้" |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python - <<'PY'
import json
import re
from pathlib import Path
root = Path(".")
th_candidates = list(root.rglob("locales/th/translation.json"))
ja_candidates = list(root.rglob("locales/ja/translation.json"))
if not th_candidates or not ja_candidates:
raise SystemExit("required locale files not found: locales/th/translation.json and/or locales/ja/translation.json")
th_path = th_candidates[0]
ja_path = ja_candidates[0]
token_re = re.compile(r"\{\{[^{}]+\}\}")
def flatten(obj, prefix=""):
out = {}
if isinstance(obj, dict):
for k, v in obj.items():
p = f"{prefix}.{k}" if prefix else k
out.update(flatten(v, p))
else:
out[prefix] = obj
return out
ja = flatten(json.loads(ja_path.read_text(encoding="utf-8")))
th = flatten(json.loads(th_path.read_text(encoding="utf-8")))
missing_in_th = sorted(set(ja) - set(th))
extra_in_th = sorted(set(th) - set(ja))
placeholder_mismatch = []
for k in sorted(set(ja) & set(th)):
if isinstance(ja[k], str) and isinstance(th[k], str):
ja_tokens = sorted(token_re.findall(ja[k]))
th_tokens = sorted(token_re.findall(th[k]))
if ja_tokens != th_tokens:
placeholder_mismatch.append((k, ja_tokens, th_tokens))
print("== missing_in_th ==")
print("\n".join(missing_in_th[:200]) or "(none)")
print("\n== extra_in_th ==")
print("\n".join(extra_in_th[:200]) or "(none)")
print("\n== placeholder_mismatch ==")
if placeholder_mismatch:
for k, jt, tt in placeholder_mismatch[:200]:
print(f"{k}\n ja={jt}\n th={tt}")
else:
print("(none)")
PYRepository: tegnike/aituber-kit
Length of output: 293
タイ語ファイルの手動編集を避け、確立されたプロセスに従ってください。
タイ語翻訳ファイル (locales/th/translation.json) の直接編集は避ける必要があります。学習ルールに基づき、翻訳は日本語ファイル (locales/ja/) のみを更新し、他言語は別の管理プロセスで処理します。
検証結果から以下の問題が検出されました:
- プレースホルダー不一致:
ConversationHistoryInfoで日本語に{{count}}が無いのに、タイ語には存在しています - 余分なキー:
MultiModalMode、MultiModalModeDescription、MultiModalModeAIDecide、MultiModalModeAlways、MultiModalModeNeverの5つのキーがタイ語に追加されていますが、日本語には未実装です
このような問題は、多言語キー整合性をチェックするCI検証で事前に検出可能です。
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@locales/th/translation.json` around lines 684 - 691, Revert the manual edits
in locales/th/translation.json and restore it to match the canonical
translations managed by the translation pipeline; specifically remove the
unintended placeholder {{count}} from the Thai "ConversationHistoryInfo" entry
so it matches the Japanese source, and delete the five added keys
("MultiModalMode", "MultiModalModeDescription", "MultiModalModeAIDecide",
"MultiModalModeAlways", "MultiModalModeNever") that are not present in the
Japanese source; instead, make any required updates in the Japanese files under
locales/ja/ and trigger the established localization process so other languages
are updated consistently.
| "GameCommentarySettings": "遊戲實況設定", | ||
| "GameCommentary": { | ||
| "Enable": "遊戲實況模式", | ||
| "EnableInfo": "以固定間隔擷取螢幕,並由 AI 即時產生與朗讀實況解說。需要支援多模態的 AI 模型。", | ||
| "CaptureInterval": "擷取間隔(秒)", | ||
| "CaptureIntervalInfo": "設定螢幕擷取的間隔({{min}}〜{{max}} 秒)。", | ||
| "ContextCount": "參考的實況歷史數", | ||
| "ContextCountInfo": "設定送給 AI 的過去實況留言數({{min}}〜{{max}} 則)。越多越能考量脈絡,但 API 費用也會增加。", | ||
| "PromptTemplate": "實況提示詞", | ||
| "PromptTemplateInfo": "AI 進行遊戲實況時使用的系統提示詞。會與角色設定的系統提示詞組合使用。\n若在輸出格式中包含 [scene] 標籤,AI 會產生畫面情景描述,並在之後的實況中作為上一次畫面狀況參考。", | ||
| "ImageQuality": "影像品質", | ||
| "ImageQualityInfo": "擷取影像的 JPEG 品質({{min}}〜{{max}})。越低資料量越小,可降低 API 費用。", | ||
| "ResizeWidth": "影像縮放寬度", | ||
| "ResizeWidthInfo": "擷取影像的縮放寬度(像素)。越小資料量越小。", | ||
| "ResizeWidthNone": "不縮放", | ||
| "SaveToChat": "將實況留言儲存到聊天紀錄", | ||
| "SaveToChatInfo": "啟用後,實況留言也會顯示在聊天紀錄中。停用時,實況留言只會保留在專用緩衝區。", | ||
| "BackgroundAnalysis": "發話中的輔助影像分析", | ||
| "BackgroundAnalysisInfo": "啟用後,在實況發話中也會低頻率分析畫面,並將結果作為輔助資訊傳給下一次實況生成。可強化場景連續性,但會產生額外的多模態 API 費用。僅建議在使用高速影像分析模型時啟用。", | ||
| "BackgroundAnalysisPromptTemplate": "輔助影像分析提示詞", | ||
| "BackgroundAnalysisPromptTemplateInfo": "發話中的輔助影像分析所使用的系統提示詞。可在此指定遊戲類型與應關注的元素。包含可辨識遊戲的資訊會更穩定。", | ||
| "BackgroundAnalysisInterval": "輔助影像分析間隔(秒)", | ||
| "BackgroundAnalysisIntervalInfo": "設定發話中每隔幾秒執行一次輔助影像分析({{min}}〜{{max}} 秒)。越短越能跟上場景,但 API 費用會增加。", | ||
| "NoCaptureWarning": "尚未開始螢幕分享。請使用選單中的「Capture」按鈕開始螢幕分享。", | ||
| "NonMultiModalWarning": "目前選擇的 AI 模型不支援多模態(影像輸入)。遊戲實況模式需要 Gemini、GPT-4o 等多模態模型。", | ||
| "DisabledByExclusion": "由於其他模式(Realtime API / 語音模式 / 外部連動 / 閒置模式)已啟用,無法使用遊戲實況模式。", | ||
| "ApiCostWarning": "⚠️ 遊戲實況模式會在每次擷取間隔呼叫多模態 API,因此會產生 API 費用。請注意。", | ||
| "Speaking": "實況中", | ||
| "Capturing": "擷取中", | ||
| "WaitingPrefix": "等待", | ||
| "CaptureUnavailable": "擷取已停止", | ||
| "VideoDelay": "影像延遲(秒)", | ||
| "VideoDelayInfo": "將螢幕分享的影像顯示延遲指定秒數({{min}}〜{{max}} 秒)。用於補償 AI 生成實況所需時間,使語音與影像時機一致。0 表示停用。送給 AI 的擷取仍會即時進行。", | ||
| "VideoDelayOff": "關閉" | ||
| }, |
There was a problem hiding this comment.
用語を既存訳に統一してください(Line 656)。
Line 656 の「外部連動」は、同ファイル内の既存用語「外部連接模式」(Line 13)と表記が揺れています。設定名の認知負荷を下げるため統一を推奨します。
✏️ 提案差分
- "DisabledByExclusion": "由於其他模式(Realtime API / 語音模式 / 外部連動 / 閒置模式)已啟用,無法使用遊戲實況模式。",
+ "DisabledByExclusion": "由於其他模式(Realtime API / 語音模式 / 外部連接 / 閒置模式)已啟用,無法使用遊戲實況模式。",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@locales/zh-TW/translation.json` around lines 631 - 665, The translation uses
inconsistent terminology: the GameCommentary.DisabledByExclusion value contains
"外部連動" but the file already uses "外部連接模式" elsewhere; update the
DisabledByExclusion string to use the existing term "外部連接模式" so wording is
consistent across the locale (search for the
"GameCommentary.DisabledByExclusion" key and replace "外部連動" with "外部連接模式").
|
Claude finished @tegnike's task —— View job PR Review: Game Commentary Mode FeatureTodo List:
📋 Review SummaryThis PR introduces a comprehensive AI-powered game commentary system that captures screen content and generates real-time commentary. The implementation demonstrates solid architectural patterns and thorough consideration of edge cases. ✅ StrengthsExcellent Architecture & Design
Comprehensive Error Handling
Strong Test Coverage
|
|
Claude finished @tegnike's task —— View job PR Review: Game Commentary Mode FeatureTodo List:
📋 Review SummaryThis PR introduces a comprehensive AI-powered game commentary system that captures screen content and generates real-time commentary. The implementation demonstrates solid architectural patterns and thorough consideration of edge cases. ✅ StrengthsExcellent Architecture & Design
Comprehensive Error Handling
Strong Test Coverage
|
|
Claude finished @tegnike's task —— View job PR Review: Game Commentary Mode FeatureTodo List:
📋 Review SummaryThis PR introduces a comprehensive AI-powered game commentary system that captures screen content and generates real-time commentary. The implementation demonstrates solid architectural patterns and thorough consideration of edge cases. ✅ StrengthsExcellent Architecture & Design
Comprehensive Error Handling
Strong Test Coverage
|
Summary by CodeRabbit
リリースノート
新機能
改善
多言語対応