feat(tui): 输入行控件(第二轮第二块)

TUI 里唯一缺的控件:读一行文本(字符追加 / Backspace 删除 / Enter 确认 / Esc 取消)。

为什么不用 Read-Host:TUI 的所有输入都走同一个 -Driver,门禁才能用 -InputScript 把"输入一个值"这一步也驱动起来 —— Read-Host 无法脚本驱动,会在门禁里挂住,而挂起比变红糟得多。

为什么 Esc 返回 $null 而不是空串:空串是"把值改成空"这个**合法意图**,取消是另一个意思,两者必须能区分,否则调用方会把"用户取消"当成"用户要清空它"。

方向键等非字符键一律忽略(在输入行里按方向键不该有副作用);字母按小写存(与键名归一化一致)。判据 8 条,全部按键序列驱动。

验收:test.ps1 9/9 全绿(5.1 与 7)。
This commit is contained in:
Shuery committed 2026-09-27 21:05:14 +08:00
1 parent 7733b1bf6d
commit 92bbc995c4
4 files changed
+54

No files matched your search

+1
View File
@@ -19,6 +19,7 @@
CompatiblePSEditions = @('Desktop', 'Core')
FunctionsToExport = @(
'Read-BakNRetLine',
'Get-BakNRetConfigSetting',
'Set-BakNRetConfigSetting',
'Invoke-BakNRetEntryScript',
+3
View File
@@ -171,7 +171,10 @@
. (Join-Path $PSScriptRoot 'Public\Get-BakNRetConfigSetting.ps1')
. (Join-Path $PSScriptRoot 'Public\Set-BakNRetConfigSetting.ps1')
. (Join-Path $PSScriptRoot 'Public\Read-BakNRetLine.ps1')
Export-ModuleMember -Function @(
'Read-BakNRetLine',
'Get-BakNRetConfigSetting',
'Set-BakNRetConfigSetting',
'Invoke-BakNRetEntryScript',
+38
View File
@@ -0,0 +1,38 @@
function Read-BakNRetLine {
<#
.SYNOPSIS
读一行文本:字符追加、Backspace 删一个、Enter 确认、Esc 取消。
.DESCRIPTION
为什么需要它、而不用 `Read-Host`:TUI 里所有输入都走同一个 `-Driver`,门禁才能用
`-InputScript` 把"输入一个值"这一步也驱动起来 —— `Read-Host` 无法脚本驱动,它会在门禁里
**挂住**,而挂起比变红糟得多。
为什么 Esc 返回 `$null` 而不是空串:空串是"把值改成空"这个**合法意图**,取消是另一个意思,
两者必须能区分 —— 否则调用方会把"用户取消"当成"用户要清空这个值"。
其它键(方向键、空格等)一律忽略:在输入行里按方向键不该有副作用。
#>
param(
[Parameter(Mandatory = $true)]$Driver,
[AllowEmptyString()][string]$Initial = ''
)
$buffer = $Initial
while ($true) {
$key = Read-BakNRetKey -Driver $Driver
if ($key -eq 'Enter') { return $buffer }
if ($key -eq 'Esc') { return $null }
if ($key -eq 'Backspace') {
if ($buffer.Length -gt 0) { $buffer = $buffer.Substring(0, $buffer.Length - 1) }
continue
}
if ($key.StartsWith('Char:')) {
$buffer += $key.Substring(5)
}
}
}
+12
View File
@@ -1774,6 +1774,18 @@ Test-Case '配置标量:只认单行标量;改一个值只动那一行;设
Assert-True $threw2 '非标量(行内哈希)也必须抛错'
}
Test-Case '输入行:字符追加、Backspace 删除、Esc 取消返回 $null(与空串区分)' {
# 全部用按键序列驱动 —— 这也是它不用 Read-Host 的原因:Read-Host 在门禁里会挂住。
Assert-Equal 'ab' (Read-BakNRetLine -Driver (New-BakNRetInputDriver -Script @('Char:a', 'Char:b', 'Enter'))) '逐字符输入'
Assert-Equal 'a' (Read-BakNRetLine -Driver (New-BakNRetInputDriver -Script @('Char:a', 'Char:b', 'Backspace', 'Enter'))) 'Backspace 删一个'
Assert-Equal '' (Read-BakNRetLine -Driver (New-BakNRetInputDriver -Script @('Backspace', 'Enter'))) '空缓冲按 Backspace 不抛错'
Assert-True ($null -eq (Read-BakNRetLine -Driver (New-BakNRetInputDriver -Script @('Char:a', 'Esc')))) 'Esc 应当返回 $null 表示取消'
Assert-Equal '' (Read-BakNRetLine -Driver (New-BakNRetInputDriver -Script @('Enter'))) '直接回车给空串(空串是合法意图)'
Assert-Equal 'xy' (Read-BakNRetLine -Driver (New-BakNRetInputDriver -Script @('Char:y', 'Enter')) -Initial 'x') '初始值之后继续追加'
Assert-Equal 'ab' (Read-BakNRetLine -Driver (New-BakNRetInputDriver -Script @('Char:a', 'Down', 'Char:b', 'Enter'))) '方向键在输入行里被忽略(无副作用)'
Assert-Equal 'k' (Read-BakNRetLine -Driver (New-BakNRetInputDriver -Script @('Char:K', 'Enter'))) '字母按小写存(与键名归一化一致)'
}
# ============================================================================
Remove-Item -LiteralPath $sandbox -Recurse -Force -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $itemSandbox -Recurse -Force -ErrorAction SilentlyContinue