feat(#57): AI抽出中のタイトル・アーティストを設定可能な配色でデコード表示#290
Conversation
LLMキャッシュミスかつAIエンドポイント設定時、API往復の間ヘッダーを processing_color(既定: 緑 #4ADE80)でスクランブル表示し、解決後に 通常色へ settle する。キャッシュヒット時は従来通り即時表示で変化なし。 - TrackUpdate に aiResolving フラグを追加 - MetadataRepository/UseCase に isAIMetadataCached を追加(キャッシュ参照のみ) - TrackInteractor: debounce 後、AI設定済み && 未キャッシュ時に aiResolving 更新を送出 - HeaderPresenter: aiResolving で startLoading(無限スクランブル)+ processingColor、 解決更新で通常色へ復帰。HeaderView は実効 titleColor/artistColor を参照 - DecodeEffect(Config) に processingColor(config key: text.decode_effect.processing_color、 solid/gradient 対応)を追加 - バージョンを 2.15.0 に更新、README/CLAUDE.md を更新
|
Warning Review limit reached
More reviews will be available in 49 minutes and 21 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughAdds an AI metadata processing indicator for LLM cache-miss scenarios. ChangesAI Processing Indicator
Sequence Diagram(s)sequenceDiagram
rect rgba(100, 149, 237, 0.5)
Note over TrackInteractorImpl,HeaderView: LLM cache miss path
end
participant TrackInteractorImpl
participant MetadataUseCaseImpl
participant llmDataStore
participant HeaderPresenter
participant HeaderView
TrackInteractorImpl->>MetadataUseCaseImpl: isAIMetadataCached(track:)
MetadataUseCaseImpl->>llmDataStore: lookup(title+artist)
llmDataStore-->>MetadataUseCaseImpl: nil (cache miss)
MetadataUseCaseImpl-->>TrackInteractorImpl: false
TrackInteractorImpl->>HeaderPresenter: emit TrackUpdate(aiResolving: true)
HeaderPresenter->>HeaderView: titleColor/artistColor = processingColor (scramble)
TrackInteractorImpl->>MetadataUseCaseImpl: resolveCandidates(track:)
MetadataUseCaseImpl-->>TrackInteractorImpl: resolved metadata
TrackInteractorImpl->>HeaderPresenter: emit TrackUpdate(aiResolving: false)
HeaderPresenter->>HeaderView: titleColor/artistColor = normal style colors (reveal)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
Tests/TrackInteractorTests/TrackInteractorAIProcessingTests.swift (1)
195-197:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winReplace fixed sleep with deadline polling in test assertion.
Line 196 uses a fixed delay before asserting sustained state. This violates the test timing guideline and can flake under variable CI load; switch to polling until a deadline.
As per coding guidelines, "
**/*Test*.swift: Do not wait on async state with fixedTask.sleepdelays in tests. Poll until a deadline instead."Suggested change
- // Sustained: the scramble never auto-settles to .revealed on its own. - try? await Task.sleep(for: .milliseconds(120)) - `#expect`(presenter.titlePhase == .revealing) - `#expect`(presenter.titleColor == Self.processing) + // Sustained: assert repeatedly until deadline instead of fixed sleep. + let deadline = ContinuousClock.now + .milliseconds(120) + while ContinuousClock.now < deadline { + `#expect`(presenter.titlePhase == .revealing) + `#expect`(presenter.titleColor == Self.processing) + try? await Task.sleep(for: .milliseconds(10)) + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Tests/TrackInteractorTests/TrackInteractorAIProcessingTests.swift` around lines 195 - 197, The test at line 196 uses a fixed Task.sleep delay before asserting on sustained state, which can cause flaky behavior under variable CI load. Replace the fixed sleep with deadline polling: remove the Task.sleep call and instead implement a polling loop that repeatedly checks the assertion condition until either it passes or a reasonable deadline is exceeded, allowing the test to complete quickly when the state is ready rather than always waiting the full fixed duration.Source: Coding guidelines
🧹 Nitpick comments (1)
Tests/MetadataUseCaseTests/MetadataUseCaseTests.swift (1)
107-107: ⚡ Quick winUse
letfor immutable mock state.Line 107 defines
aiCachedasvar, but this value is never mutated in the test flow; make itletto match the repository guideline and tighten intent.As per coding guidelines, "
**/*.swift: Preferletand functional transforms. Everyvarshould have a real reason to exist."Suggested change
-private struct MockMetadataRepository: MetadataRepository { - let candidates: [Track] - var aiCached: Bool = false +private struct MockMetadataRepository: MetadataRepository { + let candidates: [Track] + let aiCached: Bool🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Tests/MetadataUseCaseTests/MetadataUseCaseTests.swift` at line 107, Change the `aiCached` property in the MetadataUseCaseTests.swift file from `var` to `let` since this value is never mutated throughout the test flow. This aligns with the coding guideline that prefers `let` and functional transforms, ensuring that every `var` has a legitimate reason to exist. Simply replace the `var` keyword with `let` in the variable declaration.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@README.md`:
- Line 151: The README.md documentation for the processing_color configuration
parameter shows an inconsistent default value format. Update the default color
value on line 151 from `#4ADE80` to `#4ADE80FF` to align with the canonical
representation used elsewhere in this PR's documentation and internal contracts,
ensuring the alpha channel is explicitly included in all documented color
values.
In `@Tests/PresentersTests/HeaderPresenterTests.swift`:
- Line 196: The test at line 196 uses a hard-coded Task.sleep delay which can
cause flaky CI behavior. Replace the fixed millisecond delay in the Task.sleep
call with a polling mechanism that repeatedly checks for the desired async state
condition until a deadline is reached. This ensures the test waits only as long
as necessary rather than introducing arbitrary delays.
---
Outside diff comments:
In `@Tests/TrackInteractorTests/TrackInteractorAIProcessingTests.swift`:
- Around line 195-197: The test at line 196 uses a fixed Task.sleep delay before
asserting on sustained state, which can cause flaky behavior under variable CI
load. Replace the fixed sleep with deadline polling: remove the Task.sleep call
and instead implement a polling loop that repeatedly checks the assertion
condition until either it passes or a reasonable deadline is exceeded, allowing
the test to complete quickly when the state is ready rather than always waiting
the full fixed duration.
---
Nitpick comments:
In `@Tests/MetadataUseCaseTests/MetadataUseCaseTests.swift`:
- Line 107: Change the `aiCached` property in the MetadataUseCaseTests.swift
file from `var` to `let` since this value is never mutated throughout the test
flow. This aligns with the coding guideline that prefers `let` and functional
transforms, ensuring that every `var` has a legitimate reason to exist. Simply
replace the `var` keyword with `let` in the variable declaration.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: e65cb1ba-590e-4bc1-8679-75ae6da44c89
📒 Files selected for processing (28)
.claude/CLAUDE.mdREADME.mdSources/ConfigRepository/ConfigRepositoryImpl.swiftSources/Domain/Repository/MetadataRepository.swiftSources/Domain/UseCase/MetadataUseCase.swiftSources/Entity/Config/DecodeEffectConfig.swiftSources/Entity/Style/DecodeEffect.swiftSources/Entity/TrackUpdate.swiftSources/MetadataRepository/MetadataRepositoryImpl.swiftSources/MetadataUseCase/MetadataUseCaseImpl.swiftSources/Presenters/Track/HeaderPresenter.swiftSources/TrackInteractor/TrackInteractorImpl.swiftSources/VersionHandler/Resources/version.txtSources/Views/Header/HeaderView.swiftTests/ConfigDataSourceTests/ConfigTemplateTests.swiftTests/ConfigRepositoryTests/ConfigRepositoryTests.swiftTests/EntityTests/DecodeEffectConfigTests.swiftTests/LyricsUseCaseTests/LyricsSearchServiceTests.swiftTests/LyricsUseCaseTests/LyricsServiceTests.swiftTests/MetadataRepositoryTests/MetadataRepositoryTests.swiftTests/MetadataUseCaseTests/MetadataUseCaseTests.swiftTests/PresentersTests/HeaderPresenterTests.swiftTests/TrackHandlerTests/TrackHandlerImplTests.swiftTests/TrackInteractorTests/TrackInteractorAIProcessingTests.swiftTests/TrackInteractorTests/TrackInteractorArtworkTests.swiftTests/TrackInteractorTests/TrackInteractorPlaybackPositionTests.swiftTests/TrackInteractorTests/TrackInteractorRaceTests.swiftTests/TrackInteractorTests/TrackInteractorStyleTests.swift
README の他の色既定値(highlight グラデーション)が 8 桁 RGBA 形式で、
Entity の既定値も .solid("#4ADE80FF") であるため、表記を統一する。
固定の Task.sleep(120ms) による実時間待機を廃止し、注入した TestClock を advance してスクランブルループを複数フレーム進めても .revealing のまま settle しないことを決定的に検証する。CI のタイミング非依存になり、 ループの継続描画も網羅する。
Closes #57
概要
AI(LLM)抽出がキャッシュミスでライブ API を叩いている間、ヘッダーのタイトル/アーティストを設定可能な配色(既定: 緑
#4ADE80)でスクランブル表示し、ユーザーに「AI が処理中」だと視覚的に伝える。解決後は通常色で settle する。キャッシュヒット時・[ai]未設定時は従来通り即時表示で挙動は変わらない。ユーザーが見える流れ
[ai]設定済み かつ LLM 未キャッシュなら →processing_colorでスクランブル開始(= AI 処理中サイン)緑スクランブルが出ている時間 = ライブ API 往復の実レイテンシ(通常 0.5〜数秒)なので、一瞬の点滅ではなくはっきり見えるインジケータになる。
変更点
TrackUpdateにaiResolvingフラグ追加。DecodeEffect/DecodeEffectConfigにprocessingColor追加(config keytext.decode_effect.processing_color、solid / gradient 両対応、既定#4ADE80FF)MetadataRepository/MetadataUseCaseにisAIMetadataCached(track:)を追加(LLM キャッシュ参照のみ・DataSource は叩かない)aiConfigured && !isAIMetadataCachedのときだけaiResolving: trueのTrackUpdateを送出aiResolvingでDecodeEffectState.startLoading(無限スクランブル)+processingColorに切替、解決更新で通常色へ復帰。HeaderViewは実効titleColor/artistColor(@Published)を参照config.text.decodeEffect.processingColorをDecodeEffectにマッピングテスト
swift test全 964 件グリーン。新規・追加カバレッジ:TrackInteractorAIProcessingTests(新規)— AI 設定済み + キャッシュミスでaiResolving送出 / キャッシュヒット・AI 未設定では非送出HeaderPresenterTests— 処理中はprocessingColorで持続スクランブル(自発 settle しない)/ 解決後に通常色へ復帰 / 非 AI 更新は通常色のままMetadataRepositoryTests/MetadataUseCaseTests—isAIMetadataCached(キャッシュのみ参照を検証)DecodeEffectConfigTests(新規)—processing_colorの decode / 既定フォールバック / encode round-tripConfigRepositoryTests—processing_colorがDecodeEffectまで流れるConfigTemplateTests— TOML / JSON テンプレートスナップショットにprocessing_colorを反映スクリーンショットについて
本機能の視覚状態(緑スクランブル)は 実 API キー + 再生中の曲 + LLM キャッシュミス が揃ったライブ API 往復中にのみ現れるため、バックグラウンドセッションの VM / ホスト環境では狙ったフレームを安定して再現・撮影できない。挙動はユニットテストで網羅済み。通常利用では数秒間はっきり表示される。
Summary by CodeRabbit
New Features
Configuration
processing_colorkey under[text.decode_effect]section to customize the title/artist color displayed during AI resolution.