From 81c5e96fc77e5f81d5abd6a260160ce0d73604ce Mon Sep 17 00:00:00 2001 From: Tomas Kracmar Date: Fri, 7 Aug 2026 11:53:05 +0200 Subject: [PATCH] feat(tui): add fzf-based folder browser for export/import path prompts Read-Host has no tab-completion (that's a top-level PSReadLine feature, not available to script prompts), so reuse the fzf dependency already required for menus to browse folders interactively instead: navigate into subfolders, go up with .., confirm current folder, or type a path manually. Falls back to plain Read-Host when fzf isn't installed. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG_macOS_IntuneToolkit.md | 7 +++ README.md | 2 +- Scripts/Private/Start-IntuneManagementTui.ps1 | 45 ++++++++++++++++--- VERSION | 2 +- 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/CHANGELOG_macOS_IntuneToolkit.md b/CHANGELOG_macOS_IntuneToolkit.md index 56c3f10..5470498 100644 --- a/CHANGELOG_macOS_IntuneToolkit.md +++ b/CHANGELOG_macOS_IntuneToolkit.md @@ -1,5 +1,12 @@ # macOS Intune Toolkit Changelog +## 2026-08-07 — v4.4.0 — Folder browser for Export/Import path prompts + +### Added +- **`Scripts/Private/Start-IntuneManagementTui.ps1`** + - New `Select-FolderPath` helper: when `fzf` is available, the Export/Import root folder prompt (and the report flow's export/backup-root/output-dir prompts) becomes an interactive folder browser — navigate into subfolders, go up with `..`, confirm the current folder, or type a path manually. `Read-Host` alone has no tab-completion (that's a top-level PSReadLine feature, not available to script prompts), so this reuses the `fzf` dependency already required for menus instead of building custom completion. + - Falls back to a plain `Read-Host` prompt when `fzf` isn't installed — no regression from prior behavior. + ## 2026-08-07 — v4.3.1 — Fix redundant action prompt ### Fixed diff --git a/README.md b/README.md index 5208aee..169d470 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Cross-platform, headless Intune policy export/import with PowerShell. -**Current version:** `4.3.1` — see [`CHANGELOG_macOS_IntuneToolkit.md`](CHANGELOG_macOS_IntuneToolkit.md) for recent changes. +**Current version:** `4.4.0` — see [`CHANGELOG_macOS_IntuneToolkit.md`](CHANGELOG_macOS_IntuneToolkit.md) for recent changes. This repository is now CLI-first. The old WPF application surface has been removed from the repo. The supported workflow is: diff --git a/Scripts/Private/Start-IntuneManagementTui.ps1 b/Scripts/Private/Start-IntuneManagementTui.ps1 index 89fd143..e24bb55 100644 --- a/Scripts/Private/Start-IntuneManagementTui.ps1 +++ b/Scripts/Private/Start-IntuneManagementTui.ps1 @@ -107,6 +107,41 @@ function Select-MenuItem return Show-NumberedMenu -Items $Items -Header $Header -Multi:$Multi } +function Select-FolderPath +{ + param( + [Parameter(Mandatory)] + [string]$Prompt, + [string]$StartPath = (Get-Location).Path + ) + if(-not (Test-FzfAvailable)) + { + return Read-Host $Prompt + } + + $current = if(Test-Path -LiteralPath $StartPath) { (Resolve-Path -LiteralPath $StartPath).Path } else { (Get-Location).Path } + + while($true) + { + $entries = @("[Use this folder]", "[Type path manually]") + $parent = Split-Path -Path $current -Parent + if($parent) { $entries += ".." } + $subdirs = Get-ChildItem -LiteralPath $current -Directory -ErrorAction SilentlyContinue | Sort-Object Name | ForEach-Object { "$($_.Name)/" } + $entries += $subdirs + + $choice = $entries | fzf --header="$Prompt [current: $current]" + if(-not $choice) { return $null } + + switch ($choice) + { + "[Use this folder]" { return $current } + "[Type path manually]" { $typed = Read-Host "Enter path"; if(-not [string]::IsNullOrWhiteSpace($typed)) { return $typed }; continue } + ".." { $current = $parent; continue } + default { $current = Join-Path $current ($choice.TrimEnd('/')); continue } + } + } +} + function Read-YesNo { param( @@ -275,18 +310,18 @@ while($true) if([string]::IsNullOrWhiteSpace($tenantIdForReport)) { Write-Host "Tenant ID is required." -ForegroundColor Red; continue } } - $exportPath = Read-Host "Export path (where to save fresh data)" + $exportPath = Select-FolderPath -Prompt "Select export path (where to save fresh data)" if([string]::IsNullOrWhiteSpace($exportPath)) { Write-Host "Export path is required." -ForegroundColor Red; continue } $backupRoot = $exportPath } else { - $backupRoot = Read-Host "Backup root path (folder containing 'Settings Catalog', etc.)" + $backupRoot = Select-FolderPath -Prompt "Select backup root (folder containing 'Settings Catalog', etc.)" if([string]::IsNullOrWhiteSpace($backupRoot)) { Write-Host "Backup root is required." -ForegroundColor Red; continue } if(-not (Test-Path $backupRoot)) { Write-Host "Path not found: $backupRoot" -ForegroundColor Red; continue } } - $outputDir = Read-Host "Enter output directory for reports" + $outputDir = Select-FolderPath -Prompt "Select output directory for reports" if([string]::IsNullOrWhiteSpace($outputDir)) { Write-Host "Output directory is required." -ForegroundColor Red; continue } $includeAssignmentsInSettings = $false @@ -358,8 +393,8 @@ while($true) if(-not $typeSelection) { continue } # 4. Path -$pathPrompt = if($action -eq "Export") { "Enter export root folder path" } else { "Enter import root folder path" } -$path = Read-Host $pathPrompt +$pathPrompt = if($action -eq "Export") { "Select export root folder" } else { "Select import root folder" } +$path = Select-FolderPath -Prompt $pathPrompt if([string]::IsNullOrWhiteSpace($path)) { Write-Host "Path is required." -ForegroundColor Red; return $null } # 5. Name Filter diff --git a/VERSION b/VERSION index f77856a..fdc6698 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.3.1 +4.4.0