Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
53a53fd
docs: add Phase B7 checklist for RichHome layout/badge/naming/artwork…
tukuyomil032 Jul 31, 2026
77383b0
refactor: rename AtollStyleExpandedView to RichHomeView
tukuyomil032 Jul 31, 2026
eed3b29
fix: vertically center the lyrics/events column against the taller no…
tukuyomil032 Jul 31, 2026
bcc90c3
docs: check off B7-1 and B7-2 in progress.md
tukuyomil032 Jul 31, 2026
d3aee25
feat: add breathing room between the artwork row and the seek bar
tukuyomil032 Jul 31, 2026
70ce418
feat: drop the source badge's circular backdrop, enlarge the icon
tukuyomil032 Jul 31, 2026
f54f250
docs: check off B7-3 and B7-4 in progress.md
tukuyomil032 Jul 31, 2026
a206251
fix: don't permanently mark a Spotify track's artwork URL as tried on…
tukuyomil032 Jul 31, 2026
1e7ba74
fix: retry Apple Music artwork fetch on the existing position poll
tukuyomil032 Jul 31, 2026
80ac75c
docs: check off B7-5 and B7-6 in progress.md, close out Phase B7
tukuyomil032 Jul 31, 2026
393f946
docs: add Phase B8 checklist for lyrics windowing, centering, header …
tukuyomil032 Jul 31, 2026
f6a4525
fix: replace scrolling lyrics with a fixed 4-line window
tukuyomil032 Jul 31, 2026
a4ed7e4
fix: compensate lyrics column centering for the header sitting above …
tukuyomil032 Jul 31, 2026
34f1e60
fix: center the seek bar between the artwork row and controls
tukuyomil032 Jul 31, 2026
c261396
fix: shift track info up and let the album name wrap fully
tukuyomil032 Jul 31, 2026
89632df
feat: remove the redundant close button from the expanded header
tukuyomil032 Jul 31, 2026
22a0de7
feat: tighten spacing between Home/AI Usage header icons
tukuyomil032 Jul 31, 2026
0c5dec6
docs: check off Phase B8 tasks in progress.md
tukuyomil032 Jul 31, 2026
eff58c9
docs: add Phase B9 checklist for CFBundleIdentifier fix and layout tw…
tukuyomil032 Jul 31, 2026
9163764
fix: add the missing CFBundleIdentifier to Info.plist
tukuyomil032 Jul 31, 2026
ec71b55
fix: nudge track info down 10pt from the artwork's top edge
tukuyomil032 Jul 31, 2026
ea8afe3
fix: slow down lyrics active-line and window-swap animations
tukuyomil032 Jul 31, 2026
15499f4
docs: check off Phase B9 tasks in progress.md
tukuyomil032 Jul 31, 2026
04e4946
docs: add Phase B10 checklist for Automation permission fix and diagn…
tukuyomil032 Jul 31, 2026
155b74b
fix: add the missing NSAppleEventsUsageDescription
tukuyomil032 Jul 31, 2026
d8f1b21
feat: log AppleScript failures in NowPlayingManager.runAppleScript
tukuyomil032 Jul 31, 2026
3f9efe8
feat: log AppleScript failures in ArtworkFetcher
tukuyomil032 Jul 31, 2026
a78aabd
feat: left-align lyrics text instead of centering it
tukuyomil032 Jul 31, 2026
6f2c42c
docs: check off Phase B10 tasks in progress.md
tukuyomil032 Jul 31, 2026
6a5ed03
docs: add Phase B11 checklist for Settings Permissions tab
tukuyomil032 Jul 31, 2026
a039945
feat: add PermissionsStore for Calendar/Screen Recording/Automation s…
tukuyomil032 Jul 31, 2026
364997c
feat: add a Permissions tab to Settings
tukuyomil032 Jul 31, 2026
475e133
feat: add localized strings for the Permissions tab
tukuyomil032 Jul 31, 2026
436d9c0
docs: check off Phase B11 tasks in progress.md
tukuyomil032 Jul 31, 2026
9640677
docs: add Phase B12 checklist for Settings width and lyrics revert
tukuyomil032 Jul 31, 2026
43cbe3c
fix: widen the Settings window so all 7 tabs fit without overflowing
tukuyomil032 Jul 31, 2026
516ee86
fix: revert lyrics text back to center alignment
tukuyomil032 Jul 31, 2026
3007938
feat: increase lyrics font size from 13pt to 15pt
tukuyomil032 Jul 31, 2026
541c6ce
docs: check off Phase B12 tasks in progress.md
tukuyomil032 Jul 31, 2026
d22c4e2
docs: add Phase B13 checklist for exclusive source selection and star…
tukuyomil032 Jul 31, 2026
e036b21
fix: raise lyrics lineLimit from 2 to 3 to stop truncation at the lar…
tukuyomil032 Jul 31, 2026
982809c
feat: replace independent Now Playing source toggles with an exclusiv…
tukuyomil032 Jul 31, 2026
3d2e8b3
feat: migrate the legacy source toggles to preferredNowPlayingSource
tukuyomil032 Jul 31, 2026
e190f0c
docs: check off Phase B13 tasks, survey next-phase candidates
tukuyomil032 Jul 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 156 additions & 0 deletions docs/progress.md
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,162 @@ Phase B5完了後、実機で追加のフィードバックが8件出た。う

Phase B6は8タスク全完了。実機検証は次回実機起動時に通しで実施予定。

### Phase B7: RichHomeレイアウト微調整 + バッジ + 命名整理 + アートワーク安定化(2026-07-31〜)

Phase B6完了後の実機フィードバックで、歌詞ボックスの垂直センタリング未解決(横ではなく縦方向)、
シークバーとアートワークの間隔、再生元バッジの不要な黒円+アイコンサイズ、
`AtollStyleExpandedView`という参考先アプリ名を含む命名の中立化、Spotify/Apple Musicの
アートワーク取得不安定の計6件の指摘があった。詳細な真因分析は
`~/.claude/plans/phasec-phaseb-b-docs-playful-cocoa.md`のPhase B7セクション参照。

- [x] B7-1: 命名整理 — `AtollStyleExpandedView` → `RichHomeView`(ファイル名・型名・
12ファイルのコメント参照)に機械的リネーム。過去のGit履歴は書き換えない
(AskUserQuestionでユーザーと合意済み)。コミット`77383b0`。
- [x] B7-2: 歌詞ボックスの縦方向センタリング — `RichHomeView.swift`のHStack
`alignment: .top` → `.center`。あわせて`.frame(idealWidth: SurfaceMetrics.baseContentWidth)`
追加で横方向のfixedSize/nilプロポーザル起因の中央寄せ崩れも修正。コミット`eed3b29`。
- [x] B7-3: シークバーとアートワークの間隔調整 — `progressSection`に`.padding(.top, 8)`追加。
コミット`d3aee25`。
- [x] B7-4: 再生元バッジの黒円削除+アイコン拡大(18pt→40pt)。コミット`70ce418`。
- [x] B7-5: Spotifyアートワークキャッシュの成功後書き込み化(`ArtworkFetcher.swift`)。
コミット`a206251`。
- [x] B7-6: Apple Musicアートワークのバウンド付き再試行(`NowPlayingManager.swift`)。
`pollAppleMusicPosition()`の既存1.5秒間隔ポーリングに相乗りし上限3回まで再試行。
コミット`1e7ba74`。

Phase B7は6タスク全完了。実機検証は次回実機起動時に通しで実施予定
(歌詞ボックスの垂直/水平センタリング、シークバー間隔、バッジ、Spotify/Apple Music
アートワーク安定性を重点確認)。

### Phase B8: 歌詞ウィンドウ表示化 + 垂直センタリング再修正 + ヘッダー整理(2026-07-31〜)

Phase B7の実機検証で、歌詞ボックスの垂直中央がまだズレている(今度は下寄り、ヘッダー分の高さを
考慮していなかった)、歌詞前後の行が透けて見える(`.mask`によるフェードが実質縁取りのみで
opacityが0にならない設計だった)、シークバーがまだアートワークに近すぎる(非対称パディング)、
再生元バッジ拡大に伴う曲名エリアとの近接、ヘッダーの閉じるボタンが不要、ModuleSwitcherの
アイコン間隔が広すぎる、の6件の指摘があった。詳細な真因分析は
`~/.claude/plans/phasec-phaseb-b-docs-playful-cocoa.md`のPhase B8セクション参照。

- [x] B8-1: 歌詞ウィンドウ表示化 — `LyricsView.swift`をScrollView+mask方式から固定4行の
ウィンドウ表示に作り替え、前後行の透け見えを根治。コミット`f6a4525`。
- [x] B8-2: 歌詞ボックスの垂直センタリング再修正 — `SurfaceMetrics.headerHeight`定数を追加し
`RichHomeView.swift`のcenterColumnに`.offset(y: -headerHeight/2)`でヘッダー分を補正。
コミット`a4ed7e4`。
- [x] B8-3: シークバーをartwork行とcontrolsSectionの中間に揃える —
`progressSection`の`.padding(.top, 8)` → `.padding(.vertical, 8)`。コミット`34f1e60`。
- [x] B8-4: trackInfoを上にずらす+アルバム名の改行制限撤廃 — 上側Spacer削除、
アルバムの`.lineLimit(2)`削除。コミット`c261396`。
- [x] B8-5: ヘッダーの閉じるボタン削除(`IslandTopBar.swift`)。コミット`89632df`。
- [x] B8-6: ModuleSwitcherのアイコン間隔を詰める(spacing 24→8)。コミット`22a0de7`。

Phase B8は6タスク全完了。実機検証は次回実機起動時に通しで実施予定
(歌詞の垂直位置・4行固定表示・シークバー位置・trackInfo位置・ヘッダーの見た目を重点確認)。

### Phase B9: CFBundleIdentifier欠落修正 + trackInfo位置微調整 + 歌詞遷移速度調整(2026-07-31〜)

Phase B8実機検証で「`just run`起動だとアートワーク取得・コントロールボタンが効かないが、
Command+Space直接起動だとどちらも動く」という不可解な報告があった。調査の結果、
`perch/Resources/Info.plist`に`CFBundleIdentifier`が定義されておらず、ビルドごとに異なる
仮識別子・署名アイデンティティが生成されていたことが判明——macOSのTCC(Automation権限)は
署名アイデンティティ単位でも権限を区別するため、`/Applications`のad-hoc版と`run.sh`が起動する
開発証明書版が別アプリとして扱われ、片方にしかSpotify/Music操作の許可が付与されていなかった。
詳細は`~/.claude/plans/phasec-phaseb-b-docs-playful-cocoa.md`のPhase B9セクション参照。

- [x] B9-1: `perch/Resources/Info.plist`に`CFBundleIdentifier`
(`$(PRODUCT_BUNDLE_IDENTIFIER)`)を追加——起動方法によるアートワーク/コントロール
不整合の根治。実機で`Identifier=com.tukuyomi032.perch`への反映を確認済み。
コミット`9163764`。
- [x] B9-2: trackInfoの垂直位置を10pt下げる(Phase B8で上に寄せすぎた)。コミット`ec71b55`。
- [x] B9-3: 歌詞のアクティブ行切り替え・ウィンドウ遷移アニメーションを緩やかにする
(spring response 0.40→0.55、easeInOut duration 0.4→0.55)。コミット`ea8afe3`。

Phase B9は3タスク全完了。B9-1は過去に断片化したTCC Automation権限までは自動修復しないため、
実機検証時に再度の許可ダイアログが出る可能性がある旨をユーザーに案内する必要あり。

### Phase B10: Automation権限プロンプト欠落修正 + AppleScript失敗の診断強化 + 歌詞左寄せ(2026-07-31〜)

Phase B9でCFBundleIdentifierを修正した後、`just run`・直接起動どちらでもアートワーク取得・
再生コントロールが全く動作しなくなった。調査の結果、`perch/Resources/Info.plist`に
`NSAppleEventsUsageDescription`が無く、macOSがAutomation権限プロンプト自体を表示できず
AppleScript呼び出しが即座に権限拒否(-1743)で失敗する状態だったことが判明。加えて
AppleScript失敗時のログが一切無く原因切り分けができなかったため診断ログを追加する。
歌詞テキストの水平配置も中央揃え→左寄せへの変更依頼があった。詳細は
`~/.claude/plans/phasec-phaseb-b-docs-playful-cocoa.md`のPhase B10セクション参照。

- [x] B10-1: `NSAppleEventsUsageDescription`をInfo.plistに追加
(Automation権限プロンプトが表示されない根本原因の修正)。実機ビルドで反映確認済み。
コミット`155b74b`。
- [x] B10-2: `NowPlayingManager.runAppleScript`にエラー診断ログを追加。コミット`d8f1b21`。
- [x] B10-3: `ArtworkFetcher`に`Logger`を追加し各AppleScript失敗パスにログを追加。
コミット`3f9efe8`。
- [x] B10-4: 歌詞テキストを中央揃え→左寄せに変更(`LyricsView.swift`)。コミット`a78aabd`。

Phase B10は4タスク全完了。B10-1は実機で許可ダイアログが表示され、許可後にアートワーク取得・
コントロールボタンが動作することの確認が必要。

### Phase B11: 設定画面「権限」タブ新設(2026-07-31〜)

Phase B10の`NSAppleEventsUsageDescription`追加後も実機でAutomation権限エラー(-1743)が
継続。TCCの既知の挙動(コード署名が変わると過去の許可/拒否記録が新しいビルドに引き継がれず、
かつOSが自動で再プロンプトを出さない)が原因と判明。AskUserQuestionで相談の結果、設定画面に
「権限」タブを新設し、Calendar/Screen Recording/Automation(Spotify・Apple Music)の状態確認・
付与を行えるようにする方針で合意。詳細は
`~/.claude/plans/phasec-phaseb-b-docs-playful-cocoa.md`のPhase B11セクション参照。

**OSレベルの制約(コードでは解決不可)**: 既に拒否済みの権限はアプリから再プロンプトを強制
できない。ユーザーには本タスク実装を待たずにシステム設定でMusicトグルをオフ→オン、または
`tccutil reset AppleEvents com.tukuyomi032.perch`実行後の再起動を案内済み。

- [x] B11-1: `PermissionsStore`新規実装(Calendar/Screen Recording/Automation×2の状態確認・
付与ロジック)。コミット`a039945`。
- [x] B11-2: `SettingsView`に`PermissionsTab`追加。コミット`364997c`。
- [x] B11-3: ローカライズ文字列追加(項目名・ボタン文言)。コミット`475e133`。

Phase B11は3タスク全完了。実機検証時、Automation - Apple Musicが「拒否済み」表示になっている
はずなので、「システム設定を開く」ボタンでAutomationペインが実際に開くこと、システム設定側で
トグルをオフ→オンにした後「権限」タブを開き直すと状態が更新されることを確認する。

### Phase B12: 設定ウィンドウ拡幅 + 歌詞を中央揃えに戻してフォント拡大(2026-07-31〜)

Phase B11でタブが7つに増えたが`SettingsView.swift`のウィンドウ幅(420pt)が据え置きのままで、
「権限」「アップデート」タブが`>>`オーバーフローに追いやられ開けない状態になっていた。あわせて
Phase B10で左寄せにした歌詞テキストを中央揃えに戻し、フォントサイズも拡大する。詳細は
`~/.claude/plans/phasec-phaseb-b-docs-playful-cocoa.md`のPhase B12セクション参照。

- [x] B12-1: 設定ウィンドウの幅を拡大しタブ折り返しを解消(420pt→600pt)。コミット`43cbe3c`。
- [x] B12-2: 歌詞テキストを左寄せ→中央揃えに戻す。コミット`516ee86`。
- [x] B12-3: 歌詞フォントサイズを13pt→15ptに拡大。コミット`3007938`。

Phase B12は3タスク全完了。実機検証時、設定画面7タブ全てがオーバーフロー無しで開けること、
歌詞が中央揃え・大きめフォントで表示され`lyricsColumnHeight`(130pt)枠からはみ出していないことを
確認する。

### Phase B13: Now Playingソース排他選択化 + 起動時アクティブ検知 + PR作成・マージ(2026-07-31〜)

設定画面のNow Playingソーストグル(Spotify/Apple Music/YouTube Music)が実際の動作に反映
されていない、Atoll型の排他Picker(自動含む4択)にしたい、Perch起動時に既に再生中の音楽が
検知されない(一度停止するまで検知されない)、の3件を修正。加えてPhase B12のフォントサイズ
拡大で歌詞の長い行が省略記号で切り詰められる問題(`lineLimit`緩和で対応)も修正。全完了後PRを
作成しmainへマージする。詳細は`~/.claude/plans/phasec-phaseb-b-docs-playful-cocoa.md`の
Phase B13セクション参照。

- [x] B13-1: 歌詞`lineLimit`緩和(省略記号切り詰めの解消)。コミット`e036b21`。
- [x] B13-2〜7: `NowPlayingSourcePreference`新規enum + `Defaults.Key`追加 + 旧3Bool削除、
`SettingsView`のNowPlayingTabを排他Pickerに置き換え、`applyState`の排他ゲート化、
設定変更の即時反映(Defaults監視+currentStateクリア)、起動時・切替時のアクティブ検知
(Spotify/Apple Music/YouTube Music)、ローカライズ文字列更新。一体の変更のため
1コミットにまとめた。コミット`982809c`。
- [x] B13-3(移行処理+テスト、上記とは別コミット): 旧3Boolトグルから
`preferredNowPlayingSource`への移行処理を追加、`PreferencesMigrationTests`に
純粋関数マッピング・移行実行・冪等性のテストを追加。コミット`3d2e8b3`。
- [ ] B13-9: PR作成 → レビュー登録 → マージ

Phase B4〜B13の一連の実機フィードバック対応はこれでB13-9(PR作成・マージ)を残すのみ。
次フェーズ候補の棚卸し: Phase D(バッテリー監視)・Phase E(WiFi拡張)・Phase F(メディア
プレイヤー)はいずれも「未着手・タスク分割のみ」で緊急性なし。**Phase C(波形の実音キャプチャ
修理)はC1・C2のみ完了・C0とC3〜C9が未着手**——ユーザー希望のオーディオスペクトラム完成と
一致する、優先度上も次に着手して差し支えない状態。

### Phase D: バッテリー監視・アニメーション本格実装(未着手・タスク分割のみ)

**参照**: `docs/macOS-Battery-Monitoring-Animation-Handbook-ja.md`(全39章、
Expand Down
36 changes: 36 additions & 0 deletions perch/Core/NowPlayingSourcePreference.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Foundation

/// Which Now Playing source(s) Perch pays attention to, set from Settings' Now Playing tab.
///
/// `.auto` keeps the original behavior — whichever source is actually playing wins, via
/// `NowPlayingManager`'s existing priority arbitration (`sourcePriority(_:)`). Picking a
/// specific source makes it exclusive: activity from every other source is ignored
/// entirely, matching the single-select "Music Source" picker in comparable apps.
enum NowPlayingSourcePreference: String, CaseIterable, Sendable, Identifiable {
case auto
case spotify
case appleMusic
case youTubeMusic

nonisolated var id: Self { self }

nonisolated var displayNameKey: String {
switch self {
case .auto: "settings.nowplaying_source.auto"
case .spotify: "settings.spotify"
case .appleMusic: "settings.apple_music"
case .youTubeMusic: "settings.youtube_music"
}
}

/// The `MusicSource` this preference restricts activity to, or `nil` for `.auto`
/// (no restriction — every source is allowed through).
nonisolated var exclusiveSource: MusicSource? {
switch self {
case .auto: nil
case .spotify: .spotify
case .appleMusic: .appleMusic
case .youTubeMusic: .youTubeMusic
}
}
}
11 changes: 7 additions & 4 deletions perch/Core/Preferences.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,26 @@ import Foundation
/// in that file, so the vendored-adjacent source stays free of Perch's persistence choice.
extension ScreenPreference: Defaults.Serializable {}
extension UpdateChannel: Defaults.Serializable {}
extension NowPlayingSourcePreference: Defaults.Serializable {}

extension Defaults.Keys {
static let launchAtLogin = Key<Bool>("launchAtLogin", default: false)
static let showInAllSpaces = Key<Bool>("showInAllSpaces", default: true)
static let autoCollapseDelay = Key<Double>("autoCollapseDelay", default: 3.0)
static let showNowPlayingSource = Key<Bool>("showNowPlayingSource", default: false)
static let enableSpotify = Key<Bool>("enableSpotify", default: true)
static let enableAppleMusic = Key<Bool>("enableAppleMusic", default: true)
static let enableYouTubeMusic = Key<Bool>("enableYouTubeMusic", default: true)
/// Replaces the old `enableSpotify`/`enableAppleMusic`/`enableYouTubeMusic` triple of
/// independent toggles; existing installs are migrated once at launch by
/// `PreferencesMigration`.
static let preferredNowPlayingSource = Key<NowPlayingSourcePreference>(
"preferredNowPlayingSource", default: .auto)
static let languageCode = Key<String>("languageCode", default: "en")
static let updateChannel = Key<UpdateChannel>("updateChannel", default: .stable)
static let aiRefreshInterval = Key<RefreshInterval>("aiRefreshInterval", default: .fiveMinutes)
/// How the island chrome presents itself. Replaces `notchSimulationMode`; existing
/// installs are migrated once at launch by `PreferencesMigration`.
static let islandChromeStyle = Key<IslandChromeStyle>("islandChromeStyle", default: .notch)

/// Rich (Atoll-inspired, default) vs Minimal (the original preset-driven widget list).
/// Rich (the fuller default layout) vs Minimal (the original preset-driven widget list).
static let uiMode = Key<UIMode>("uiMode", default: .rich)

/// Which display the island lives on. Resolved through `ScreenLocator` and handed to
Expand Down
Loading
Loading