diff --git a/windows/README.md b/windows/README.md new file mode 100644 index 0000000..5762a2b --- /dev/null +++ b/windows/README.md @@ -0,0 +1,123 @@ +# Windows configuration + +Live on the `windows` branch. Branches from `main`, so shared configs under +`config/` (nvim, tmux, etc.) are reusable — `setup.ps1` symlinks them into +their Windows locations. + +## Layout + +``` +windows/ +├── powershell/ +│ ├── Microsoft.PowerShell_profile.ps1 # used by both pwsh 7 and PS 5.1 +│ └── Modules/ +│ └── KrisTools/ # personal cmdlets (see below) +├── windows-terminal/ +│ └── settings.json +├── git/ +│ ├── .gitconfig +│ └── ignore # global gitignore +├── scoop/ +│ ├── config.json +│ └── apps.json # `scoop export` +├── winget/ +│ └── apps.json # `winget export` +└── setup.ps1 +``` + +## Bootstrap a new machine + +1. Install [scoop](https://scoop.sh) and/or ensure winget is present. +2. Install git and pwsh: + ```powershell + scoop install git pwsh + ``` +3. Clone: + ```powershell + git clone -b windows https://github.com/kriswill/dotfiles "$HOME\src\dotfiles" + cd "$HOME\src\dotfiles" + ``` +4. Allow symlink creation via **one** of: + - **Elevated shell** (simplest on a fresh box): right-click Windows + Terminal / PowerShell → *Run as administrator*. Works from either + Windows PowerShell 5.1 or pwsh 7 — confirmed working on Win11. + - **Developer Mode on** (no admin needed per run): Settings → Privacy + & security → For developers → **Developer Mode = On**. +5. Run setup (from the elevated shell, if you went that route): + ```powershell + pwsh -ExecutionPolicy Bypass -File .\windows\setup.ps1 -InstallApps + ``` + If pwsh isn't installed yet, the script also runs fine under Windows + PowerShell 5.1: + ```powershell + powershell -ExecutionPolicy Bypass -File .\windows\setup.ps1 -InstallApps + ``` + +`setup.ps1` backs up any existing file to `.bak-` before +replacing it with a symlink. Safe to re-run. + +## Custom commands (KrisTools module) + +Personal utilities are packaged as a PowerShell module rather than a +`bin/` directory of loose scripts — it's the idiomatic Windows answer: +approved verbs, `Get-Help`, tab completion, in-process invocation, no +`PATHEXT` tricks. + +Layout: + +``` +powershell/Modules/KrisTools/ +├── KrisTools.psd1 # manifest +├── KrisTools.psm1 # loader (dot-sources Public/*.ps1) +└── Public/ + └── Show-DisplayInfo.ps1 # function + Set-Alias display-info +``` + +`setup.ps1` symlinks the `KrisTools` directory into the user's +`Documents\PowerShell\Modules\` (and the 5.1 and OneDrive-redirected +variants). PowerShell auto-discovers modules on `$env:PSModulePath` so +no config changes are needed beyond `Import-Module KrisTools` in the +profile. + +### Adding a new command + +1. Create `powershell/Modules/KrisTools/Public/Verb-Noun.ps1` using an + [approved verb](https://learn.microsoft.com/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands): + ```powershell + function Verb-Noun { + [CmdletBinding()] + param() + # ... + } + Set-Alias -Name short-name -Value Verb-Noun + ``` +2. That's it — `KrisTools.psm1` auto-loads every `Public\*.ps1` on + `Import-Module`, and the manifest exports all functions and aliases. +3. Re-run `setup.ps1` only if you added a *new module directory*. + +### Current commands + +| Command | Alias | What it does | +| --------------- | -------------- | ---------------------------------- | +| `Show-DisplayInfo` | `display-info` | Styled GPU & monitor report | + +## Related guides + +- [nerdfont-setup.md](./nerdfont-setup.md) — install pwsh 7, the + `NerdFonts` PSGallery module, JetBrainsMono Nerd Font, and wire it into + Windows Terminal as the default profile + font. + +## Capturing changes back to the repo + +Because targets are symlinks, edits made in Windows Terminal / PowerShell +/ `.gitconfig` land directly in the repo — just `git add` + commit. + +The scoop import currently includes `gh` (GitHub CLI), `lazygit`, and +`pwsh`. Add more by `scoop install ` then re-exporting. + +Refresh app lists after installing something new: + +```powershell +scoop export > windows\scoop\apps.json +winget export -o windows\winget\apps.json +``` diff --git a/windows/git/.gitconfig b/windows/git/.gitconfig new file mode 100644 index 0000000..b61aefa --- /dev/null +++ b/windows/git/.gitconfig @@ -0,0 +1,10 @@ +[filter "lfs"] + required = true + clean = git-lfs clean -- %f + smudge = git-lfs smudge -- %f + process = git-lfs filter-process +[user] + name = Kris Williams + email = 115474+kriswill@users.noreply.github.com +[safe] + directory = C:/Program Files (x86)/World of Warcraft/_retail_ diff --git a/windows/git/ignore b/windows/git/ignore new file mode 100644 index 0000000..593c885 --- /dev/null +++ b/windows/git/ignore @@ -0,0 +1 @@ +**/.claude\settings.local.json diff --git a/windows/nerdfont-setup.md b/windows/nerdfont-setup.md new file mode 100644 index 0000000..e2196bd --- /dev/null +++ b/windows/nerdfont-setup.md @@ -0,0 +1,117 @@ +# Nerd Font setup on Windows (PowerShell only) + +End state: PowerShell 7 installed, `JetBrainsMono Nerd Font` installed, Windows Terminal using it as default with a PowerShell 7 profile as default. + +## 1. Install PowerShell 7 + +From **Windows PowerShell 5.1** (run as your user, not admin): + +```powershell +winget install --id Microsoft.PowerShell --source winget --accept-source-agreements --accept-package-agreements +``` + +Alternative if you use scoop: + +```powershell +scoop install pwsh +``` + +Close this shell. Everything from here on runs in **pwsh** (PowerShell 7). + +## 2. Launch pwsh and bootstrap PSGallery + +Open a new `pwsh` session: + +```powershell +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser +Set-PSRepository -Name PSGallery -InstallationPolicy Trusted +``` + +Notes: +- The `NerdFonts` module uses PowerShell 7.3+ syntax (`clean {}` block). It will **not** load under Windows PowerShell 5.1 despite what its manifest claims. That's why step 1 installs pwsh first. + +## 3. Install the NerdFonts module + +```powershell +Install-Module -Name NerdFonts -Scope CurrentUser -Force +Import-Module NerdFonts +Get-Command -Module NerdFonts # should show Get-NerdFont, Install-NerdFont +``` + +## 4. Install JetBrainsMono Nerd Font + +```powershell +Get-NerdFont | Where-Object Name -like 'JetBrains*' # confirm name +Install-NerdFont -Name 'JetBrainsMono' +``` + +Use `-Scope AllUsers` (admin required) to install system-wide instead of per-user. Verify: + +```powershell +(New-Object System.Drawing.Text.InstalledFontCollection).Families | + Where-Object Name -like '*JetBrains*' +``` + +You should see `JetBrainsMono Nerd Font` (and possibly `JetBrainsMono Nerd Font Mono`, `…Propo`). + +## 5. Auto-import on future sessions (optional) + +Create the pwsh profile: + +```powershell +New-Item -ItemType File -Path $PROFILE -Force +@' +if (-not (Get-Module -ListAvailable -Name NerdFonts)) { + Install-Module -Name NerdFonts -Scope CurrentUser -Force -ErrorAction SilentlyContinue +} +Import-Module NerdFonts -ErrorAction SilentlyContinue +'@ | Set-Content -Path $PROFILE -Encoding UTF8 +``` + +## 6. Configure Windows Terminal + +Locate settings.json: + +```powershell +$wtSettings = "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json" +``` + +Open it in your editor and: + +**a.** Set the font for all profiles — add a `font.face` entry inside `profiles.defaults`: + +```json +"defaults": { + "font": { "face": "JetBrainsMono Nerd Font" } +} +``` + +**b.** Add a PowerShell 7 profile to `profiles.list` (adjust `commandline` if pwsh is not on PATH — e.g. scoop installs at `%USERPROFILE%\scoop\apps\pwsh\current\pwsh.exe`): + +```json +{ + "guid": "{2d8e4a3f-7c91-4e5b-a6f2-8b1c5d9e3a7b}", + "name": "PowerShell 7", + "commandline": "pwsh.exe -NoLogo", + "startingDirectory": "%USERPROFILE%", + "hidden": false +} +``` + +Generate your own GUID with `[guid]::NewGuid()` if you prefer. + +**c.** Set it as default — change the top-level `defaultProfile` to match the GUID above: + +```json +"defaultProfile": "{2d8e4a3f-7c91-4e5b-a6f2-8b1c5d9e3a7b}" +``` + +Open a new Windows Terminal tab — it should launch pwsh 7 with JetBrainsMono Nerd Font. + +## Troubleshooting + +- **`Get-NerdFont` not found** — module didn't install. Re-run step 2, then `Install-Module NerdFonts -Force -Verbose` and read the error. +- **Font not showing in Terminal** — face name mismatch. Run the `InstalledFontCollection` check above and use the exact `Name` value. +- **`clean {}` parser error** — you're in Windows PowerShell 5.1. Switch to pwsh. +- **OneDrive-redirected Documents** — `$PROFILE` will resolve to `…\OneDrive\Documents\PowerShell\…`. That's fine; `Install-Module` installs to `…\Documents\PowerShell\Modules\` (same root), so pwsh finds it. diff --git a/windows/powershell/Microsoft.PowerShell_profile.ps1 b/windows/powershell/Microsoft.PowerShell_profile.ps1 new file mode 100644 index 0000000..9917a2b --- /dev/null +++ b/windows/powershell/Microsoft.PowerShell_profile.ps1 @@ -0,0 +1,10 @@ +if (-not (Get-Module -ListAvailable -Name NerdFonts)) { + try { + Install-Module -Name NerdFonts -Scope CurrentUser -Force -AcceptLicense -ErrorAction Stop + } catch { + Write-Warning "Failed to install NerdFonts module: $_" + } +} +Import-Module NerdFonts -ErrorAction SilentlyContinue + +Import-Module KrisTools -ErrorAction SilentlyContinue diff --git a/windows/powershell/Modules/KrisTools/KrisTools.psd1 b/windows/powershell/Modules/KrisTools/KrisTools.psd1 new file mode 100644 index 0000000..83ae937 --- /dev/null +++ b/windows/powershell/Modules/KrisTools/KrisTools.psd1 @@ -0,0 +1,12 @@ +@{ + RootModule = 'KrisTools.psm1' + ModuleVersion = '0.1.0' + GUID = 'a9e1c6d4-0b27-4f3c-9c5a-7f2d8e4b1a03' + Author = 'Kris Williams' + Description = 'Personal PowerShell utilities (dotfiles/windows).' + PowerShellVersion = '5.1' + FunctionsToExport = '*' + AliasesToExport = '*' + CmdletsToExport = @() + VariablesToExport = @() +} diff --git a/windows/powershell/Modules/KrisTools/KrisTools.psm1 b/windows/powershell/Modules/KrisTools/KrisTools.psm1 new file mode 100644 index 0000000..8a5e13e --- /dev/null +++ b/windows/powershell/Modules/KrisTools/KrisTools.psm1 @@ -0,0 +1,7 @@ +$Public = @(Get-ChildItem -Path "$PSScriptRoot\Public\*.ps1" -ErrorAction SilentlyContinue) + +foreach ($file in $Public) { + . $file.FullName +} + +Export-ModuleMember -Function $Public.BaseName -Alias * diff --git a/windows/powershell/Modules/KrisTools/Public/Show-DisplayInfo.ps1 b/windows/powershell/Modules/KrisTools/Public/Show-DisplayInfo.ps1 new file mode 100644 index 0000000..1e04196 --- /dev/null +++ b/windows/powershell/Modules/KrisTools/Public/Show-DisplayInfo.ps1 @@ -0,0 +1,314 @@ +function Show-DisplayInfo { + <# + .SYNOPSIS + GPU & monitor configuration report for the current Windows machine. + .DESCRIPTION + Renders a styled report using Nerd Font icons. Requires Windows Terminal + with ANSI support and a Nerd Font (e.g. JetBrainsMono Nerd Font). Pulls + data from Win32_VideoController, WmiMonitorID, nvidia-smi, the NVIDIA + registry, and (optionally) ~/dxdiag_report.txt. .EXAMPLE + Show-DisplayInfo + .EXAMPLE + display-info + #> + [CmdletBinding()] + param() + + # display-info.ps1 - GPU & Monitor Configuration Report + # Requires: Nerd Font, Windows Terminal with ANSI support + + $ESC = [char]27 + + # -- Color palette ----------------------------------------------------------- + $Reset = "$ESC[0m" + $Bold = "$ESC[1m" + $Dim = "$ESC[2m" + + $Green = "$ESC[38;2;118;210;117m" + $Cyan = "$ESC[38;2;86;214;214m" + $Yellow = "$ESC[38;2;229;192;123m" + $Orange = "$ESC[38;2;209;154;102m" + $Red = "$ESC[38;2;224;108;117m" + $Blue = "$ESC[38;2;97;175;239m" + $Purple = "$ESC[38;2;198;120;221m" + $White = "$ESC[38;2;220;223;228m" + $DimWhite = "$ESC[38;2;140;143;148m" + $NvGreen = "$ESC[38;2;118;185;0m" + + # -- Nerd Font Icons --------------------------------------------------------- + $IconGpu = [char]::ConvertFromUtf32(0xF108D) + $IconMonitor = [char]::ConvertFromUtf32(0xF0379) + $IconRefresh = [char]::ConvertFromUtf32(0xF046B) + $IconCheck = [char]::ConvertFromUtf32(0xF05E0) + $IconDisplay = [char]::ConvertFromUtf32(0xF0878) + $IconChip = [char]0xF2DB + $IconBolt = [char]0xF0E7 + $IconPalette = [char]::ConvertFromUtf32(0xF03D8) + $IconConnect = [char]::ConvertFromUtf32(0xF0337) + $IconInfo = [char]0xEA74 + $IconWarning = [char]0xF071 + $IconHdr = [char]::ConvertFromUtf32(0xF0A60) + $IconSync = [char]0xF021 + $IconRotate = [char]::ConvertFromUtf32(0xF01BB) + $IconThermo = [char]0xF2C9 + $IconMem = [char]::ConvertFromUtf32(0xF035B) + $IconDriver = [char]0xF013 + + # -- Box-drawing characters -------------------------------------------------- + $TL = [char]0x256D + $TR = [char]0x256E + $BL = [char]0x2570 + $BR = [char]0x256F + $H = [char]0x2500 + $V = [char]0x2502 + $LT = [char]0x251C + $RT = [char]0x2524 + $DH = [char]0x2550 + $DV = [char]0x2551 + $DTL = [char]0x2554 + $DTR = [char]0x2557 + $DBL = [char]0x255A + $DBR = [char]0x255D + $Deg = [char]0x00B0 + + # -- Visual Width Calculation ------------------------------------------------ + # Windows Terminal treats PUA (both BMP U+E000-U+F8FF and supplementary planes + # 15/16) as Unicode "ambiguous" width, which defaults to narrow = 1 column. + # So every Nerd Font icon, surrogate pair or not, counts as 1 visual column. + function Get-VisualWidth([string]$text) { + $stripped = $text -replace "$([char]27)\[[0-9;]*m", "" + $w = 0 + $i = 0 + while ($i -lt $stripped.Length) { + $ch = $stripped[$i] + if ([char]::IsHighSurrogate($ch) -and ($i + 1) -lt $stripped.Length -and [char]::IsLowSurrogate($stripped[$i + 1])) { + # One codepoint (surrogate pair) = 1 visual column + $w += 1 + $i += 2 + } else { + $w += 1 + $i++ + } + } + return $w + } + + function PadTo([string]$text, [int]$targetWidth) { + $vw = Get-VisualWidth $text + $pad = $targetWidth - $vw + if ($pad -lt 0) { $pad = 0 } + return "$text$(' ' * $pad)" + } + + # -- Box Layout -------------------------------------------------------------- + # Row format: V + space + [icon] + space + [label padded] + [value padded] + V + # Total visual = 1 + 1 + iconW + 1 + labelTextW + valueW + 1 = W + + $W = 80 # total box width in columns + $LabelTextW = 16 # label text width (without icon) + + function BoxRow([string]$icon, [string]$label, [string]$value, [string]$labelColor, [string]$valueColor, [string]$borderColor) { + $iconW = Get-VisualWidth $icon + $valW = $W - $iconW - $LabelTextW - 4 # V+sp+icon+sp+label+value+V = W + $lbl = PadTo "$labelColor$label$Reset" $LabelTextW + $val = PadTo "$valueColor$value$Reset" $valW + return "$borderColor$V$Reset $labelColor$icon$Reset $lbl$val$borderColor$V$Reset" + } + + function SectionHeader([string]$icon, [string]$title, [string]$color) { + $iconW = Get-VisualWidth $icon + $contentLen = $iconW + 1 + $title.Length # icon + space + title + $leftPad = 2 + $rightPad = $W - 2 - $leftPad - $contentLen - 2 # -2 for spaces around content + if ($rightPad -lt 0) { $rightPad = 0 } + return "$color$LT$("$H" * $leftPad)$Reset $Bold$color$icon $title$Reset $color$("$H" * $rightPad)$RT$Reset" + } + + function HBar([string]$left, [string]$right, [string]$color) { + return "$color$left$("$H" * ($W - 2))$right$Reset" + } + + # -- Data Collection --------------------------------------------------------- + Write-Host "" + Write-Host " $Dim${IconChip} Collecting system display information...$Reset" + Write-Host "" + + # GPU info + $gpu = Get-CimInstance Win32_VideoController | Select-Object -First 1 + $gpuName = $gpu.Name + $driverVer = $gpu.DriverVersion + + # nvidia-smi data + $nvData = $null + try { + $nvRaw = nvidia-smi --query-gpu=name,driver_version,vbios_version,memory.total,temperature.gpu,power.draw,clocks.current.graphics,clocks.current.memory --format=csv,noheader,nounits 2>$null + if ($nvRaw) { + $parts = $nvRaw -split "," + $nvData = @{ + Name = $parts[0].Trim() + Driver = $parts[1].Trim() + VBIOS = $parts[2].Trim() + VRAM = $parts[3].Trim() + Temp = $parts[4].Trim() + Power = $parts[5].Trim() + ClockGfx = $parts[6].Trim() + ClockMem = $parts[7].Trim() + } + } + } catch {} + + # Monitor info via WMI + $monitors = @() + try { + $monIds = Get-CimInstance -Namespace root\wmi -ClassName WmiMonitorID -ErrorAction Stop + foreach ($mon in $monIds) { + $mfr = ($mon.ManufacturerName | Where-Object {$_ -ne 0} | ForEach-Object {[char]$_}) -join '' + $model = ($mon.UserFriendlyName | Where-Object {$_ -ne 0} | ForEach-Object {[char]$_}) -join '' + $serial= ($mon.SerialNumberID | Where-Object {$_ -ne 0} | ForEach-Object {[char]$_}) -join '' + $monitors += @{ Manufacturer = $mfr; Model = $model; Serial = $serial } + } + } catch {} + + # DxDiag data + $dxPath = Join-Path $env:USERPROFILE "dxdiag_report.txt" + $dxDisplays = @() + if (Test-Path $dxPath) { + $dxContent = Get-Content $dxPath -Raw + $sections = $dxContent -split "(?=Display Devices)" | Select-Object -Skip 1 + foreach ($section in $sections) { + $cardName = if ($section -match "Card name:\s*(.+)") { $Matches[1].Trim() } else { "Unknown" } + $currentMode = if ($section -match "Current Mode:\s*(.+)") { $Matches[1].Trim() } else { "Unknown" } + $hdrSupport = if ($section -match "HDR Support:\s*(.+)") { $Matches[1].Trim() } else { "Unknown" } + $monName = if ($section -match "Monitor Model:\s*(.+)") { $Matches[1].Trim() } else { "Unknown" } + $outputType = if ($section -match "Output Type:\s*(.+)") { $Matches[1].Trim() } else { "Unknown" } + $dedMem = if ($section -match "Dedicated Memory:\s*(.+)") { $Matches[1].Trim() } else { "Unknown" } + $monCaps = if ($section -match "Monitor Capabilities:\s*(.+)") { $Matches[1].Trim() } else { "" } + $dxDisplays += @{ + Card = $cardName; Mode = $currentMode; HDR = $hdrSupport + MonModel = $monName; Output = $outputType; DedMemory = $dedMem; MonCaps = $monCaps + } + } + } + + # G-Sync registry + $gsyncEnabled = $false + try { + $nvTweak = Get-ItemProperty "HKLM:\SOFTWARE\NVIDIA Corporation\Global\NVTweak" -ErrorAction SilentlyContinue + if ($nvTweak.OverridePRR -eq 1) { $gsyncEnabled = $true } + } catch {} + + # -- Render ------------------------------------------------------------------ + + # Title banner + $titleText = "DISPLAY CONFIGURATION REPORT" + $bannerInner = $W - 2 + $bannerPad = [math]::Floor(($bannerInner - $titleText.Length) / 2) + $bannerPadR = $bannerInner - $titleText.Length - $bannerPad + + Write-Host "" + Write-Host " $NvGreen$DTL$("$DH" * $bannerInner)$DTR$Reset" + Write-Host " $NvGreen$DV$Reset$(' ' * $bannerPad)$Bold$NvGreen$titleText$Reset$(' ' * $bannerPadR)$NvGreen$DV$Reset" + Write-Host " $NvGreen$DBL$("$DH" * $bannerInner)$DBR$Reset" + Write-Host "" + + # -- GPU Section ------------------------------------------------------------- + Write-Host " $(HBar $TL $TR $Green)" + Write-Host " $(SectionHeader $IconChip 'GPU' $Green)" + Write-Host " $(HBar $LT $RT $Green)" + + $gpuDisplayName = if ($nvData) { $nvData.Name } else { $gpuName } + Write-Host (" " + (BoxRow $IconChip "Card" $gpuDisplayName $Yellow $White $Green)) + + $vramText = if ($nvData) { "$($nvData.VRAM) MB GDDR7" } else { "$([math]::Round($gpu.AdapterRAM / 1MB)) MB" } + Write-Host (" " + (BoxRow $IconMem "VRAM" $vramText $Yellow $White $Green)) + + $drvText = if ($nvData) { "$($nvData.Driver) ($driverVer)" } else { $driverVer } + Write-Host (" " + (BoxRow $IconDriver "Driver" $drvText $Yellow $White $Green)) + + if ($nvData) { + Write-Host (" " + (BoxRow $IconInfo "VBIOS" $nvData.VBIOS $Yellow $DimWhite $Green)) + Write-Host (" " + (BoxRow $IconThermo "Temperature" "$($nvData.Temp)${Deg}C" $Yellow $Cyan $Green)) + Write-Host (" " + (BoxRow $IconBolt "Power Draw" "$($nvData.Power) W" $Yellow $Cyan $Green)) + Write-Host (" " + (BoxRow $IconRefresh "GPU Clock" "$($nvData.ClockGfx) MHz" $Yellow $Cyan $Green)) + Write-Host (" " + (BoxRow $IconMem "Mem Clock" "$($nvData.ClockMem) MHz" $Yellow $Cyan $Green)) + } + + Write-Host " $(HBar $BL $BR $Green)" + Write-Host "" + + # -- G-Sync Section ---------------------------------------------------------- + Write-Host " $(HBar $TL $TR $Purple)" + Write-Host " $(SectionHeader $IconSync 'G-SYNC / VRR' $Purple)" + Write-Host " $(HBar $LT $RT $Purple)" + + if ($gsyncEnabled) { + $gsyncVal = "$Green$IconCheck Enabled$Reset" + } else { + $gsyncVal = "$Red$IconWarning Unknown$Reset" + } + Write-Host (" " + (BoxRow $IconSync "G-Sync" $gsyncVal $Yellow $White $Purple)) + Write-Host (" " + (BoxRow $IconDisplay "Mode" "Windowed + Fullscreen" $Yellow $White $Purple)) + Write-Host (" " + (BoxRow $IconInfo "PRR Override" "Active (OverridePRR=1)" $Yellow $DimWhite $Purple)) + + Write-Host " $(HBar $BL $BR $Purple)" + Write-Host "" + + # -- Monitor Sections -------------------------------------------------------- + $monitorDefs = @( + @{ + Label = "PRIMARY MONITOR" + Color = $Cyan + Rows = @( + ,@($IconMonitor, "Model", "ASUS ROG Swift OLED PG34WCDM", "$Bold$White") + ,@($IconDisplay, "Panel", "34`" W-OLED, 800R Curve", $White) + ,@($IconRefresh, "Resolution", "3440 x 1440 @ 240 Hz", $White) + ,@($IconRotate, "Orientation", "Landscape", $White) + ,@($IconHdr, "HDR", "$Green$IconCheck Supported$Reset (BT2020, PQ, TrueBlack 400)", $White) + ,@($IconSync, "VRR", "G-Sync Compatible + FreeSync Premium Pro", $White) + ,@($IconBolt, "VRR Range", "40-240 Hz (LFC)", $White) + ,@($IconRefresh, "Response", "0.03 ms GTG", $White) + ,@($IconPalette, "Color", "99% DCI-P3, 135% sRGB, 10-bit", $White) + ,@($IconHdr, "Brightness", "1300 nits peak (3% window HDR)", $White) + ,@($IconConnect, "Connection", "DisplayPort 1.4 (DSC)", $White) + ,@($IconInfo, "Features", "ELMB, Smart KVM, USB-C 90W PD", $DimWhite) + ,@($IconCheck, "Status", "$Green$IconCheck Active$Reset", $White) + ) + }, + @{ + Label = "SECONDARY MONITOR" + Color = $Blue + Rows = @( + ,@($IconMonitor, "Model", "ASUS ROG Swift PG348Q", "$Bold$White") + ,@($IconDisplay, "Panel", "34`" IPS, 3800R Curve", $White) + ,@($IconRefresh, "Resolution", "1440 x 3440 @ 60 Hz", $White) + ,@($IconRotate, "Orientation", "$Yellow$IconRotate Portrait (90${Deg})$Reset", $White) + ,@($IconHdr, "HDR", "$DimWhite$IconWarning Not Supported$Reset", $White) + ,@($IconSync, "VRR", "G-Sync (Hardware Module)", $White) + ,@($IconBolt, "VRR Range", "30-60 Hz (degraded, was 30-100)", $White) + ,@($IconRefresh, "Response", "5 ms", $White) + ,@($IconPalette, "Color", "100% sRGB, 10-bit", $White) + ,@($IconHdr, "Brightness", "300 nits", $White) + ,@($IconConnect, "Connection", "DisplayPort", $White) + ,@($IconInfo, "Features", "Turbo Key refresh toggle", $DimWhite) + ,@($IconCheck, "Status", "$Yellow$IconWarning Degraded$Reset (OC disabled, max 60 Hz)", $White) + ) + } + ) + + foreach ($mon in $monitorDefs) { + $c = $mon.Color + Write-Host " $(HBar $TL $TR $c)" + Write-Host " $(SectionHeader $IconMonitor $mon.Label $c)" + Write-Host " $(HBar $LT $RT $c)" + + foreach ($row in $mon.Rows) { + Write-Host (" " + (BoxRow $row[0] $row[1] $row[2] $Yellow $row[3] $c)) + } + + Write-Host " $(HBar $BL $BR $c)" + Write-Host "" + } + + Write-Host ""} + +Set-Alias -Name display-info -Value Show-DisplayInfo diff --git a/windows/scoop/apps.json b/windows/scoop/apps.json new file mode 100644 index 0000000..f604985 --- /dev/null +++ b/windows/scoop/apps.json @@ -0,0 +1,46 @@ +{ + "buckets": [ + { + "Name": "main", + "Source": "https://github.com/ScoopInstaller/Main.git", + "Updated": "2026-04-12T13:31:36-07:00", + "Manifests": 1493 + }, + { + "Name": "extras", + "Source": "https://github.com/ScoopInstaller/Extras", + "Updated": "2026-04-12T14:46:08-07:00", + "Manifests": 2291 + } + ], + "apps": [ + { + "Version": "2.89.0", + "Info": "", + "Source": "main", + "Name": "gh", + "Updated": "2026-04-12T17:44:01.8307756-07:00" + }, + { + "Version": "0.56.0", + "Info": "", + "Source": "extras", + "Name": "lazygit", + "Updated": "2025-11-24T23:00:04.2500043-08:00" + }, + { + "Version": "0.12.1", + "Info": "", + "Source": "main", + "Name": "neovim", + "Updated": "2026-04-12T17:44:01.8307756-07:00" + }, + { + "Version": "7.6.0", + "Info": "", + "Source": "main", + "Name": "pwsh", + "Updated": "2026-04-12T16:49:15.0726375-07:00" + } + ] +} diff --git a/windows/scoop/config.json b/windows/scoop/config.json new file mode 100644 index 0000000..f98fa37 --- /dev/null +++ b/windows/scoop/config.json @@ -0,0 +1,5 @@ +{ + "last_update": "2026-04-12T16:49:07.0177779-07:00", + "scoop_repo": "https://github.com/ScoopInstaller/Scoop", + "scoop_branch": "master" +} diff --git a/windows/setup.ps1 b/windows/setup.ps1 new file mode 100644 index 0000000..57f7f26 --- /dev/null +++ b/windows/setup.ps1 @@ -0,0 +1,136 @@ +#Requires -Version 5.1 +<# +.SYNOPSIS + Bootstrap this machine's Windows config from the dotfiles repo. + +.DESCRIPTION + Symlinks profile/terminal/git config from this repo into their expected + Windows locations. Backs up any pre-existing file to .bak- + before replacing. Optionally installs apps from scoop/winget export files. + + Run from an elevated PowerShell OR enable Developer Mode + (Settings -> Privacy & security -> For developers) so New-Item -SymbolicLink + works without admin. + +.PARAMETER InstallApps + Also run `scoop import` and `winget import` from the exported lists. + +.PARAMETER DotfilesRoot + Path to the dotfiles checkout. Defaults to the parent of this script. +#> +[CmdletBinding()] +param( + [switch]$InstallApps, + [string]$DotfilesRoot = (Split-Path -Parent (Split-Path -Parent $PSCommandPath)) +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +$WindowsDir = Join-Path $DotfilesRoot 'windows' +if (-not (Test-Path $WindowsDir)) { + throw "Expected windows/ dir at $WindowsDir" +} + +function New-Symlink { + param( + [Parameter(Mandatory)][string]$LinkPath, + [Parameter(Mandatory)][string]$TargetPath + ) + + if (-not (Test-Path $TargetPath)) { + Write-Warning "Skip: target missing $TargetPath" + return + } + + $parent = Split-Path -Parent $LinkPath + if (-not (Test-Path $parent)) { + New-Item -ItemType Directory -Path $parent -Force | Out-Null + } + + if (Test-Path $LinkPath) { + $item = Get-Item $LinkPath -Force + if ($item.LinkType -eq 'SymbolicLink' -and $item.Target -contains $TargetPath) { + Write-Host "OK $LinkPath -> $TargetPath" -ForegroundColor Green + return + } + $stamp = Get-Date -Format 'yyyyMMdd-HHmmss' + $backup = "$LinkPath.bak-$stamp" + Write-Host "Backup $LinkPath -> $backup" -ForegroundColor Yellow + Move-Item -Path $LinkPath -Destination $backup -Force + } + + New-Item -ItemType SymbolicLink -Path $LinkPath -Target $TargetPath | Out-Null + Write-Host "Link $LinkPath -> $TargetPath" -ForegroundColor Cyan +} + +# --- PowerShell profile (pwsh 7 and Windows PowerShell 5.1 share the same file) --- +$profileSource = Join-Path $WindowsDir 'powershell\Microsoft.PowerShell_profile.ps1' +$profileTargets = @( + "$HOME\Documents\PowerShell\Microsoft.PowerShell_profile.ps1", + "$HOME\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1", + "$HOME\OneDrive\Documents\PowerShell\Microsoft.PowerShell_profile.ps1", + "$HOME\OneDrive\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1" +) +foreach ($t in $profileTargets) { + if (Test-Path (Split-Path -Parent $t)) { + New-Symlink -LinkPath $t -TargetPath $profileSource + } +} + +# --- PowerShell custom modules (KrisTools, etc.) --- +# Link each module dir into the user's per-host Modules path. Both pwsh 7 +# and Windows PowerShell 5.1 auto-discover modules placed there. +$moduleSource = Join-Path $WindowsDir 'powershell\Modules' +if (Test-Path $moduleSource) { + $moduleTargets = @( + "$HOME\Documents\PowerShell\Modules", + "$HOME\Documents\WindowsPowerShell\Modules", + "$HOME\OneDrive\Documents\PowerShell\Modules", + "$HOME\OneDrive\Documents\WindowsPowerShell\Modules" + ) + foreach ($modDir in (Get-ChildItem -Directory $moduleSource)) { + foreach ($base in $moduleTargets) { + if (Test-Path (Split-Path -Parent $base)) { + New-Symlink -LinkPath (Join-Path $base $modDir.Name) -TargetPath $modDir.FullName + } + } + } +} + +# --- Windows Terminal --- +$wtSource = Join-Path $WindowsDir 'windows-terminal\settings.json' +$wtTarget = "$env:LOCALAPPDATA\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json" +if (Test-Path (Split-Path -Parent $wtTarget)) { + New-Symlink -LinkPath $wtTarget -TargetPath $wtSource +} + +# --- Git --- +New-Symlink -LinkPath "$HOME\.gitconfig" -TargetPath (Join-Path $WindowsDir 'git\.gitconfig') +New-Symlink -LinkPath "$HOME\.config\git\ignore" -TargetPath (Join-Path $WindowsDir 'git\ignore') + +# --- Scoop config --- +New-Symlink -LinkPath "$HOME\.config\scoop\config.json" -TargetPath (Join-Path $WindowsDir 'scoop\config.json') + +# --- Shared (cross-platform) configs from repo root --- +# Neovim, tmux, etc. live at repo root under config/ on main. +$nvimSource = Join-Path $DotfilesRoot 'config\nvim' +if (Test-Path $nvimSource) { + New-Symlink -LinkPath "$env:LOCALAPPDATA\nvim" -TargetPath $nvimSource +} + +if ($InstallApps) { + $scoopApps = Join-Path $WindowsDir 'scoop\apps.json' + if ((Get-Command scoop -ErrorAction SilentlyContinue) -and (Test-Path $scoopApps)) { + Write-Host "scoop import $scoopApps" -ForegroundColor Cyan + scoop import $scoopApps + } + + $wingetApps = Join-Path $WindowsDir 'winget\apps.json' + if ((Get-Command winget -ErrorAction SilentlyContinue) -and (Test-Path $wingetApps)) { + Write-Host "winget import $wingetApps" -ForegroundColor Cyan + winget import -i $wingetApps --accept-package-agreements --accept-source-agreements + } +} + +Write-Host "`nDone." -ForegroundColor Green diff --git a/windows/windows-terminal/settings.json b/windows/windows-terminal/settings.json new file mode 100644 index 0000000..1e42efb --- /dev/null +++ b/windows/windows-terminal/settings.json @@ -0,0 +1,77 @@ +{ + "$help": "https://aka.ms/terminal-documentation", + "$schema": "https://aka.ms/terminal-profiles-schema", + "actions": [], + "copyFormatting": "none", + "copyOnSelect": false, + "defaultProfile": "{574e775e-4f2a-5b96-ac1e-a2962a402336}", + "keybindings": + [ + { + "id": "Terminal.CopyToClipboard", + "keys": "ctrl+c" + }, + { + "id": "Terminal.PasteFromClipboard", + "keys": "ctrl+v" + }, + { + "id": "Terminal.FindText", + "keys": "ctrl+shift+f" + }, + { + "id": "Terminal.DuplicatePaneAuto", + "keys": "alt+shift+d" + } + ], + "newTabMenu": + [ + { + "type": "remainingProfiles" + } + ], + "profiles": + { + "defaults": { + "font": { + "face": "JetBrainsMono Nerd Font" + } + }, + "list": + [ + { + "commandline": "C:\\Users\\kris\\scoop\\apps\\pwsh\\current\\pwsh.exe -NoLogo", + "guid": "{2d8e4a3f-7c91-4e5b-a6f2-8b1c5d9e3a7b}", + "hidden": false, + "name": "PowerShell 7.6.0", + "startingDirectory": "%USERPROFILE%" + }, + { + "commandline": "%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", + "guid": "{61c54bbd-c2c6-5271-96e7-009a87ff44bf}", + "hidden": false, + "name": "Windows PowerShell" + }, + { + "commandline": "%SystemRoot%\\System32\\cmd.exe", + "guid": "{0caa0dad-35be-5f56-a8ff-afceeeaa6101}", + "hidden": false, + "name": "Command Prompt" + }, + { + "guid": "{b453ae62-4e3d-5e58-b989-0a998ec441b8}", + "hidden": false, + "name": "Azure Cloud Shell", + "source": "Windows.Terminal.Azure" + }, + { + "guid": "{574e775e-4f2a-5b96-ac1e-a2962a402336}", + "hidden": false, + "name": "PowerShell", + "source": "Windows.Terminal.PowershellCore" + } + ] + }, + "schemes": [], + "themes": [] +} \ No newline at end of file diff --git a/windows/winget/apps.json b/windows/winget/apps.json new file mode 100644 index 0000000..f5fef60 --- /dev/null +++ b/windows/winget/apps.json @@ -0,0 +1,86 @@ +{ + "$schema" : "https://aka.ms/winget-packages.schema.2.0.json", + "CreationDate" : "2026-04-12T17:23:16.414-00:00", + "Sources" : + [ + { + "Packages" : + [ + { + "PackageIdentifier" : "Git.Git" + }, + { + "PackageIdentifier" : "Nvidia.PhysX" + }, + { + "PackageIdentifier" : "Microsoft.Edge" + }, + { + "PackageIdentifier" : "Valve.Steam" + }, + { + "PackageIdentifier" : "Microsoft.VCRedist.2015+.x86" + }, + { + "PackageIdentifier" : "Microsoft.DotNet.DesktopRuntime.10" + }, + { + "PackageIdentifier" : "Microsoft.VCRedist.2015+.x64" + }, + { + "PackageIdentifier" : "GitHub.GitHubDesktop" + }, + { + "PackageIdentifier" : "Microsoft.VisualStudioCode" + }, + { + "PackageIdentifier" : "AgileBits.1Password" + }, + { + "PackageIdentifier" : "Anthropic.Claude" + }, + { + "PackageIdentifier" : "Microsoft.AppInstaller" + }, + { + "PackageIdentifier" : "Microsoft.UI.Xaml.2.7" + }, + { + "PackageIdentifier" : "Microsoft.UI.Xaml.2.8" + }, + { + "PackageIdentifier" : "Microsoft.VCLibs.Desktop.14" + }, + { + "PackageIdentifier" : "Microsoft.VCLibs.14" + }, + { + "PackageIdentifier" : "Microsoft.WindowsAppRuntime.1.5" + }, + { + "PackageIdentifier" : "Microsoft.WindowsAppRuntime.1.6" + }, + { + "PackageIdentifier" : "Microsoft.WindowsAppRuntime.1.7" + }, + { + "PackageIdentifier" : "Microsoft.WindowsAppRuntime.1.8" + }, + { + "PackageIdentifier" : "Microsoft.WindowsTerminal" + }, + { + "PackageIdentifier" : "TheBrowserCompany.Arc" + } + ], + "SourceDetails" : + { + "Argument" : "https://cdn.winget.microsoft.com/cache", + "Identifier" : "Microsoft.Winget.Source_8wekyb3d8bbwe", + "Name" : "winget", + "Type" : "Microsoft.PreIndexed.Package" + } + } + ], + "WinGetVersion" : "1.28.220" +} \ No newline at end of file