Powershellの配色をテーマに応じて自動で切り替える

PS本体の「アプリケーションのテーマ」だけじゃなく、ライト/ダークモードに応じて「規定の配色」を特定のものに自動で切り替えたい。
設定でどうにかするのは無理そうだったので起動時スクリプトを作って対処。

:::note warn
**`settings.json`を直接書き換えます。** 
予めバックアックを取っておいて下さい。
:::

# 環境
- Windows 11
- PowerShell 7.4.5

# 準備
- PowerShellを起動し設定 (Ctrl+,) を開く
- [配色]を開く
- 新規追加
  - ライトテーマ用の配色スキームを作り、「配色の名前を変更する」から `LightMode` にして保存
  - ダークテーマ用も同様にして `DarkMode` を作成

# スクリプト本体
### エラーアクションを設定
```powershell
$ErrorActionPreference = "Stop"
```

### 変数のパスを定義
```powershell
# レジストリ
$registryPath = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize"

#Powershellの設定ファイル (settings.json)
$settingsFilePath = "$env:LOCALAPPDATA\packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json"
```
- 「`AppsUseLightTheme` が存在しない」的なエラーが出た場合[これ](https://ascii.jp/elem/000/001/038/1038074/)を参考に作成
- ここでは `AppsUseLightTheme` を使ってるけど多分 `SystemUseLightTheme` でも良い？
- [設定>個人用設定>色>モードを選ぶ] がライトまたはダークの想定
  - カスタムになって弄られてると切り替え時にバグるかも

### テーマの確認・変更
#### Try-Catchでエラーハンドリング
```powershell
try {
    # {テーマの確認・変更}
} catch {
    Write-Error "An error occurred: $_"
}
```

#### `$theme` と `$colorScheme` を定義
```powershell
# レジストリから現在のシステムテーマを取得
$theme = Get-ItemProperty -Path $registryPath -Name "AppsUseLightTheme" -ErrorAction Stop

# 現在のシステムテーマに応じて $colorSheme を定義
$colorScheme = if ($theme.AppsUseLightTheme -eq 1) { "LightMode" } else { "DarkMode" }
```

#### settings.json を確認・読み込んでパース・`$settings` に定義
```powershell
if (-not (Test-Path $settingsFilePath)) {
    throw "Settings file not found at $settingsFilePath"
}
$settings = Get-Content -Path $settingsFilePath -Raw | ConvertFrom-Json
```

#### colorScheme を確認・システムテーマと異なるなら切り替え
```powershell
if (-not $settings.profiles) {
    throw "profiles not found in settings file."
}
if (-not $settings.profiles.defaults) {
    throw "defaults not found in profiles."
}
if (-not $settings.profiles.defaults.PSObject.Properties.Name -contains "colorScheme") {
    throw "colorScheme not found in defaults."
}
if ($settings.profiles.defaults.colorScheme -ne $colorScheme) {
    $settings.profiles.defaults.colorScheme = $colorScheme
        $jsonSettings = $settings | ConvertTo-Json -Depth 10
        [System.IO.File]::WriteAllText($settingsFilePath, $jsonSettings)
        <# Write-Host "Color scheme updated to $colorScheme"
    } else {
        Write-Host "Color scheme is already set to $colorScheme"
    #>}
} catch {
    Write-Error "An error occurred: $_"
}
```
- `profiles.defaults.colorScheme` を変更してjson丸ごと書き換え。`Depth` は実際には5もあれば足りるはず
- コメントアウト部分をアンコメントするとPowershell起動時にコンソール出力を返す
  - 配色が切り替わったときだけ出力がほしい場合一行目だけ残す

### エラーアクションをデフォルトに戻す
```powershell
$ErrorActionPreference = "Continue"
```

# 起動時スクリプトとして設定
```powershell
notepad $PROFILE
```
でプロファイルを開く。 ( `vim` でも `code` でもなんでも)
<details><summary>「みつかりません」と表示されたら</summary>

```powershell
New-Item -path $profile -type file -force
```
でプロファイルを新規に作成し、再度開く。
</details>
<br>

メモ帳が立ち上がるので
```powershell:スクリプト全体
$ErrorActionPreference = "Stop"
$settingsFilePath = "$env:LOCALAPPDATA\packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json"
$registryPath = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize"
try {
    $theme = Get-ItemProperty -Path $registryPath -Name "AppsUseLightTheme" -ErrorAction Stop
    $colorScheme = if ($theme.AppsUseLightTheme -eq 1) { "LightMode" } else { "DarkMode" }
    if (-not (Test-Path $settingsFilePath)) {
        throw "Settings file not found at $settingsFilePath"
    }
    $settings = Get-Content -Path $settingsFilePath -Raw | ConvertFrom-Json
    if (-not $settings.profiles) {
        throw "profiles not found in settings file."
    }
    if (-not $settings.profiles.defaults) {
        throw "defaults not found in profiles."
    }
    if (-not $settings.profiles.defaults.PSObject.Properties.Name -contains "colorScheme") {
        throw "colorScheme not found in defaults."
    }
    if ($settings.profiles.defaults.colorScheme -ne $colorScheme) {
        $settings.profiles.defaults.colorScheme = $colorScheme
        $jsonSettings = $settings | ConvertTo-Json -Depth 10
        [System.IO.File]::WriteAllText($settingsFilePath, $jsonSettings)
    }
} catch {
    Write-Error "An error occurred: $_"
}
```
をペースト。

<br>

:::note
プロファイルを既に色々弄っててごちゃついてる場合 `\Documents\Powershell\Scripts\` に `AutoThemeChange.ps1` なりで保存して呼び出す方がいいかもしれない
:::

# 後記
webページならCSSの `prefers-color-scheme` と[Dark Reader](https://github.com/darkreader/darkreader)、アプリケーションなら[Auto Dark Mode](https://github.com/AutoDarkMode/Windows-Auto-Night-Mode)で対処できる中、ここだけ手動が必要なのはけっこう不便。
これを使わずとも公式で機能として使えるようにしてほしい。

# 参考
- [Win32 アプリでダークモード対応 - Zenn](https://zenn.dev/tenka/articles/win32_darkmode)
- [ASCII.jp：Windows 10で設定画面を「ブラック配色」にする方法](https://ascii.jp/elem/000/001/038/1038074/)
- [Windows ターミナルでの配色 | Microsoft Learn](https://learn.microsoft.com/ja-jp/windows/terminal/customize-settings/color-schemes)
- [Windows Powershellの配色を変更する - Zenn](https://zenn.dev/n0cti1ucent/articles/789ed4b16e0851)
- [Powershellをカスタマイズしてイケイケにする方法 - Qiita](https://qiita.com/frodo821/items/ed0f1c795eaac1736f30)
- [PowerShellのprofile.ps1自分用設定 - Qiita](https://qiita.com/tomoko523/items/87ccaec05a433b02f67e)