Skip to content

Commit a2b3431

Browse files
committed
feat: 添加 --custom 自定义软件选择模式
- Bash: 方向键导航 + 空格勾选/取消(TUI 模式) - PowerShell: 编号输入 + 逗号分隔(输入模式) - 支持全选(输入 0) - 显示已安装软件标记 - README 中/英文档同步更新 - 版本号: v0.30.1
1 parent 4ef3925 commit a2b3431

6 files changed

Lines changed: 364 additions & 6 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ quickstart.sh --cfg-path /path/to/profiles.json
147147
| `--verbose` / `-v` | Show detailed debug info |
148148
| `--log-file FILE` | Write logs to file |
149149
| `--export-plan FILE` | Export installation plan to file |
150+
| `--custom` | Custom software selection mode (manual selection) |
150151
| `--list-profiles` | List all available profiles |
151152
| `--show-profile KEY` | Show profile details |
152153
| `--skip SW` | Skip specified software (repeatable) |

README.zh-CN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ quickstart.sh --cfg-path /path/to/profiles.json
141141
| `--verbose` / `-v` | 显示详细调试信息 |
142142
| `--log-file FILE` | 将日志写入文件 |
143143
| `--export-plan FILE` | 导出安装计划到文件 |
144+
| `--custom` | 自定义软件选择模式(手动选择) |
144145
| `--list-profiles` | 列出所有可用套餐 |
145146
| `--cfg-url URL` | 使用远程 profiles.json URL |
146147
| `--help` | 显示帮助 |

dist/quickstart.ps1

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ param(
1414
[switch]$verbose,
1515
[string]$logFile,
1616
[string]$exportPlan,
17+
[switch]$custom,
1718
[switch]$listProfiles,
1819
[string]$showProfile,
1920
[string[]]$skip,
@@ -488,6 +489,58 @@ function Show-ProfileMenu {
488489
return $profileData[$cursor].Key
489490
}
490491

492+
function Show-CustomMenu {
493+
param([string]$Path, [string]$OS, [string]$ProfileKey)
494+
495+
$includes = Get-ProfileIncludes -Path $Path -Key $ProfileKey
496+
if ($includes.Count -eq 0) {
497+
Write-Log "No software in profile" "WARN"
498+
return @()
499+
}
500+
501+
Write-Host ""
502+
Write-Header ($script:LANG["select_software"])
503+
Write-Host ""
504+
Write-Host " $($script:LANG["enter_numbers"])" -ForegroundColor Cyan
505+
Write-Host " 0 = $($script:LANG["select_all"])" -ForegroundColor Cyan
506+
Write-Host ""
507+
508+
# Display numbered list
509+
$swData = @()
510+
for ($i = 0; $i -lt $includes.Count; $i++) {
511+
$sw = $includes[$i]
512+
$name = Get-SoftwareField -Path $Path -Key $sw -Field "name"
513+
$desc = Get-SoftwareField -Path $Path -Key $sw -Field "desc"
514+
$installed = Test-SoftwareInstalled -Path $Path -Key $sw -OS $OS
515+
516+
$num = $i + 1
517+
$status = if ($installed) { " [$($script:LANG["skipping_installed"])]" } else { "" }
518+
Write-Host " $num. $name - $desc$status"
519+
520+
$swData += @{ Key = $sw; Name = $name; Installed = $installed }
521+
}
522+
Write-Host ""
523+
524+
# Prompt for selection
525+
$input = Read-Host "Enter numbers (comma-separated)"
526+
$selected = @()
527+
528+
if ($input -match "^[\d, ]+$") {
529+
$numbers = $input -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ -match "^\d+$" } | ForEach-Object { [int]$_ }
530+
foreach ($num in $numbers) {
531+
if ($num -eq 0) {
532+
# Select all
533+
$selected = @($includes)
534+
break
535+
} elseif ($num -ge 1 -and $num -le $swData.Count) {
536+
$selected += $swData[$num - 1].Key
537+
}
538+
}
539+
}
540+
541+
return $selected
542+
}
543+
491544
function Show-SoftwareMenu {
492545
param([string]$Path, [string]$OS, [string]$ProfileKey)
493546

@@ -995,7 +1048,11 @@ function Main {
9951048
exit 0
9961049
}
9971050

998-
$script:SELECTED_SOFTWARE = Show-SoftwareMenu -Path $script:CONFIG_FILE -OS $os -ProfileKey $script:SELECTED_PROFILES[0]
1051+
if ($custom) {
1052+
$script:SELECTED_SOFTWARE = Show-CustomMenu -Path $script:CONFIG_FILE -OS $os -ProfileKey $script:SELECTED_PROFILES[0]
1053+
} else {
1054+
$script:SELECTED_SOFTWARE = Show-SoftwareMenu -Path $script:CONFIG_FILE -OS $os -ProfileKey $script:SELECTED_PROFILES[0]
1055+
}
9991056
}
10001057

10011058
if ($script:SELECTED_SOFTWARE.Count -eq 0) {

dist/quickstart.sh

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Quickstart-PC - 一键配置新电脑
3636
--verbose, -v 显示详细调试信息
3737
--log-file FILE 将日志写入文件
3838
--export-plan FILE 导出安装计划到文件
39+
--custom 自定义软件选择模式
3940
--list-profiles 列出所有可用套餐
4041
--show-profile KEY 显示指定套餐详情
4142
--skip SW 跳过指定软件(可多次使用)
@@ -62,6 +63,7 @@ Options:
6263
--verbose, -v Show detailed debug info
6364
--log-file FILE Write logs to file
6465
--export-plan FILE Export installation plan to file
66+
--custom Custom software selection mode
6567
--list-profiles List all available profiles
6668
--show-profile KEY Show profile details
6769
--skip SW Skip specified software (repeatable)
@@ -81,6 +83,7 @@ AUTO_YES=false
8183
VERBOSE=false
8284
LOG_FILE=""
8385
EXPORT_PLAN=""
86+
CUSTOM_MODE=false
8487
LIST_PROFILES=false
8588
SHOW_PROFILE=""
8689
SKIP_SW=()
@@ -102,6 +105,7 @@ while [[ $# -gt 0 ]]; do
102105
--verbose|-v) VERBOSE=true; shift ;;
103106
--log-file) LOG_FILE="$2"; shift 2 ;;
104107
--export-plan) EXPORT_PLAN="$2"; shift 2 ;;
108+
--custom) CUSTOM_MODE=true; shift ;;
105109
--list-profiles) LIST_PROFILES=true; shift ;;
106110
--show-profile) SHOW_PROFILE="$2"; shift 2 ;;
107111
--skip) SKIP_SW+=("$2"); shift 2 ;;
@@ -915,6 +919,115 @@ install_software() {
915919
fi
916920
}
917921

922+
# 自定义软件选择模式
923+
custom_select_software() {
924+
local json_file=$1
925+
local os=$2
926+
local profile_key=$3
927+
928+
# 获取套餐包含的软件
929+
local -a sw_keys=()
930+
while IFS= read -r key; do
931+
[[ -z "$key" ]] && continue
932+
sw_keys+=("$key")
933+
done < <(json_get_profile_includes "$json_file" "$profile_key")
934+
935+
local num_sw=${#sw_keys[@]}
936+
937+
echo ""
938+
log_header "$LANG_SELECT_SOFTWARE"
939+
echo ""
940+
echo -e " ${CYAN}$LANG_NAVIGATE_MULTI${NC}"
941+
echo ""
942+
943+
# 构建菜单项
944+
local -a menu_names=()
945+
local -a checked=()
946+
947+
# 全选
948+
menu_names+=("${ORANGE}$LANG_SELECT_ALL${NC}")
949+
checked+=(0)
950+
951+
for key in "${sw_keys[@]}"; do
952+
local name=$(json_get_software_field "$json_file" "$key" "name")
953+
local desc=$(json_get_software_field "$json_file" "$key" "desc")
954+
955+
if is_installed "$json_file" "$os" "$key"; then
956+
menu_names+=("${GRAY}$name - $desc $LANG_INSTALLED${NC}")
957+
else
958+
menu_names+=("$name - $desc")
959+
fi
960+
checked+=(0)
961+
done
962+
963+
local num_items=${#menu_names[@]}
964+
local cursor=0
965+
local running=true
966+
967+
tput civis 2>/dev/null || true
968+
stty -echo 2>/dev/null
969+
970+
while [[ "$running" == "true" ]]; do
971+
printf "\r\033[2K"
972+
for ((i=0; i<num_items; i++)); do
973+
if [[ $i -eq $cursor ]]; then
974+
if [[ ${checked[$i]} -eq 1 ]]; then
975+
echo -e " ${REVERSE}${GREEN}${LANG_SELECTED}${NC}${REVERSE}${menu_names[$i]}${NC}"
976+
else
977+
echo -e " ${REVERSE}${LANG_NOT_SELECTED}${menu_names[$i]}${NC}"
978+
fi
979+
else
980+
if [[ ${checked[$i]} -eq 1 ]]; then
981+
echo -e " ${GREEN}${LANG_SELECTED}${NC}${menu_names[$i]}"
982+
else
983+
echo -e " ${LANG_NOT_SELECTED}${menu_names[$i]}"
984+
fi
985+
fi
986+
done
987+
988+
local key=""
989+
IFS= read -rsn1 key < /dev/tty
990+
991+
# 空字符串 = 回车
992+
if [[ -z "$key" ]]; then
993+
running=false
994+
# ESC 开头 = 方向键
995+
elif [[ "$key" == $'\x1b' ]]; then
996+
local seq=""
997+
IFS= read -rsn2 seq < /dev/tty
998+
if [[ "$seq" == "[A" ]]; then
999+
((cursor--))
1000+
[[ $cursor -lt 0 ]] && cursor=$((num_items - 1))
1001+
elif [[ "$seq" == "[B" ]]; then
1002+
((cursor++))
1003+
[[ $cursor -ge $num_items ]] && cursor=0
1004+
fi
1005+
# 空格 = 切换选择
1006+
elif [[ "$key" == " " ]]; then
1007+
if [[ $cursor -eq 0 ]]; then
1008+
local new_state=$((1 - checked[0]))
1009+
for ((i=0; i<num_items; i++)); do
1010+
checked[$i]=$new_state
1011+
done
1012+
else
1013+
checked[$cursor]=$((1 - checked[$cursor]))
1014+
fi
1015+
fi
1016+
1017+
# 上移光标重绘
1018+
printf '\033[%dA' "$num_items"
1019+
done
1020+
1021+
tput cnorm 2>/dev/null || true
1022+
stty echo 2>/dev/null
1023+
echo ""
1024+
1025+
SELECTED_SOFTWARE=()
1026+
for ((i=1; i<num_items; i++)); do
1027+
[[ ${checked[$i]} -eq 1 ]] && SELECTED_SOFTWARE+=("${sw_keys[$((i-1))]}")
1028+
done
1029+
}
1030+
9181031
show_banner() {
9191032
echo ""
9201033
printf "\033[0;34m╔══════════════════════════════════════╗\n"
@@ -1019,11 +1132,19 @@ main() {
10191132
fi
10201133

10211134
SELECTED_PROFILES=("$PROFILE_KEY")
1022-
show_software_menu "$CONFIG_FILE" "$os" "${SELECTED_PROFILES[@]}"
1135+
if [[ "$CUSTOM_MODE" == "true" ]]; then
1136+
custom_select_software "$CONFIG_FILE" "$os" "${SELECTED_PROFILES[@]}"
1137+
else
1138+
show_software_menu "$CONFIG_FILE" "$os" "${SELECTED_PROFILES[@]}"
1139+
fi
10231140
else
10241141
show_profile_menu "$CONFIG_FILE"
10251142
[[ ${#SELECTED_PROFILES[@]} -eq 0 ]] && log_warn "$LANG_NO_PROFILE_SELECTED" && exit 0
1026-
show_software_menu "$CONFIG_FILE" "$os" "${SELECTED_PROFILES[@]}"
1143+
if [[ "$CUSTOM_MODE" == "true" ]]; then
1144+
custom_select_software "$CONFIG_FILE" "$os" "${SELECTED_PROFILES[@]}"
1145+
else
1146+
show_software_menu "$CONFIG_FILE" "$os" "${SELECTED_PROFILES[@]}"
1147+
fi
10271148
fi
10281149

10291150
if [[ ${#SELECTED_SOFTWARE[@]} -eq 0 ]]; then

src/quickstart.ps1

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ param(
1414
[switch]$verbose,
1515
[string]$logFile,
1616
[string]$exportPlan,
17+
[switch]$custom,
1718
[switch]$listProfiles,
1819
[string]$showProfile,
1920
[string[]]$skip,
@@ -488,6 +489,58 @@ function Show-ProfileMenu {
488489
return $profileData[$cursor].Key
489490
}
490491

492+
function Show-CustomMenu {
493+
param([string]$Path, [string]$OS, [string]$ProfileKey)
494+
495+
$includes = Get-ProfileIncludes -Path $Path -Key $ProfileKey
496+
if ($includes.Count -eq 0) {
497+
Write-Log "No software in profile" "WARN"
498+
return @()
499+
}
500+
501+
Write-Host ""
502+
Write-Header ($script:LANG["select_software"])
503+
Write-Host ""
504+
Write-Host " $($script:LANG["enter_numbers"])" -ForegroundColor Cyan
505+
Write-Host " 0 = $($script:LANG["select_all"])" -ForegroundColor Cyan
506+
Write-Host ""
507+
508+
# Display numbered list
509+
$swData = @()
510+
for ($i = 0; $i -lt $includes.Count; $i++) {
511+
$sw = $includes[$i]
512+
$name = Get-SoftwareField -Path $Path -Key $sw -Field "name"
513+
$desc = Get-SoftwareField -Path $Path -Key $sw -Field "desc"
514+
$installed = Test-SoftwareInstalled -Path $Path -Key $sw -OS $OS
515+
516+
$num = $i + 1
517+
$status = if ($installed) { " [$($script:LANG["skipping_installed"])]" } else { "" }
518+
Write-Host " $num. $name - $desc$status"
519+
520+
$swData += @{ Key = $sw; Name = $name; Installed = $installed }
521+
}
522+
Write-Host ""
523+
524+
# Prompt for selection
525+
$input = Read-Host "Enter numbers (comma-separated)"
526+
$selected = @()
527+
528+
if ($input -match "^[\d, ]+$") {
529+
$numbers = $input -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ -match "^\d+$" } | ForEach-Object { [int]$_ }
530+
foreach ($num in $numbers) {
531+
if ($num -eq 0) {
532+
# Select all
533+
$selected = @($includes)
534+
break
535+
} elseif ($num -ge 1 -and $num -le $swData.Count) {
536+
$selected += $swData[$num - 1].Key
537+
}
538+
}
539+
}
540+
541+
return $selected
542+
}
543+
491544
function Show-SoftwareMenu {
492545
param([string]$Path, [string]$OS, [string]$ProfileKey)
493546

@@ -995,7 +1048,11 @@ function Main {
9951048
exit 0
9961049
}
9971050

998-
$script:SELECTED_SOFTWARE = Show-SoftwareMenu -Path $script:CONFIG_FILE -OS $os -ProfileKey $script:SELECTED_PROFILES[0]
1051+
if ($custom) {
1052+
$script:SELECTED_SOFTWARE = Show-CustomMenu -Path $script:CONFIG_FILE -OS $os -ProfileKey $script:SELECTED_PROFILES[0]
1053+
} else {
1054+
$script:SELECTED_SOFTWARE = Show-SoftwareMenu -Path $script:CONFIG_FILE -OS $os -ProfileKey $script:SELECTED_PROFILES[0]
1055+
}
9991056
}
10001057

10011058
if ($script:SELECTED_SOFTWARE.Count -eq 0) {

0 commit comments

Comments
 (0)