Add SSH port field and an edit flow for existing SSH domains

- SSH domain add flow gains a 4th, optional "port" step; a non-empty
  port is appended to remote_address as "host:port" (WezTerm's native
  way to specify a non-default SSH port), matching how existing
  entries are parsed back out via the new split_remote_address helper.
- New "SSH 도메인 수정" entry in connection settings lists existing
  config.ssh_domains entries; picking one re-prompts each field
  (name/address/port/username) with the current value shown in the
  description, where leaving a field blank keeps the old value instead
  of cancelling (unlike the add flow, where blank cancels).
- update_ssh_domain locates the exact entry block by its original name
  (pattern built from append_ssh_domain's fixed write format, escaped
  for Lua pattern safety) and replaces it wholesale, so a rename is
  just "old name matched, new block written".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-18 00:56:51 +09:00
parent 7a0304bc3d
commit 4d680aa6dd

View File

@@ -248,6 +248,19 @@ local function show_font_settings(window, pane)
)
end
local function escape_pattern(s)
return (s:gsub("[%^%$%(%)%.%[%]%*%+%-%?%%]", "%%%1"))
end
-- "host:port" 형태의 remote_address를 host, port로 분리 (port 없으면 port는 nil)
local function split_remote_address(remote_address)
local host, port = remote_address:match("^(.-):(%d+)$")
if host then
return host, port
end
return remote_address, nil
end
local function append_ssh_domain(window, pane, name, remote_address, username)
local text, err = read_config_text()
if not text then
@@ -273,13 +286,42 @@ local function append_ssh_domain(window, pane, name, remote_address, username)
window:perform_action(act.ReloadConfiguration, pane)
end
-- SSH 도메인 등록을 이름 -> 주소 -> 사용자명 순서로 한 칸씩 물어봄
local function update_ssh_domain(window, pane, old_name, name, remote_address, username)
local text, err = read_config_text()
if not text then
notify(window, err)
return
end
local quoted_old_name = escape_pattern(string.format("%q", old_name))
local pattern = "\t{\n\t\tname = " .. quoted_old_name .. ",\n\t\tremote_address = %b\"\",\n\t\tusername = %b\"\",\n\t},\n"
local entry = string.format('\t{\n\t\tname = %q,\n\t\tremote_address = %q,\n\t\tusername = %q,\n\t},\n', name, remote_address, username)
local safe_entry = entry:gsub("%%", "%%%%")
local updated, count = text:gsub(pattern, safe_entry, 1)
if count == 0 then
notify(window, "SSH 도메인 항목을 찾지 못했습니다: " .. old_name)
return
end
local ok, write_err = write_config_text(updated)
if not ok then
notify(window, write_err)
return
end
notify(window, "SSH 도메인 수정됨: " .. name .. " -> " .. remote_address .. " (WezTerm을 완전히 재시작해야 반영됩니다)")
window:perform_action(act.ReloadConfiguration, pane)
end
-- SSH 도메인 등록을 이름 -> 주소 -> 포트 -> 사용자명 순서로 한 칸씩 물어봄
-- (WezTerm에는 입력칸을 여러 개 한 화면에 배치하는 폼 위젯이 없어, 한 줄짜리
-- PromptInputLine을 단계별로 이어서 띄우는 방식으로 흉내낸다)
local ssh_domain_steps = {
{ field = "name", title = "SSH 이름 (1/3, 예: prod)" },
{ field = "remote_address", title = "SSH 주소 (2/3, 예: myserver.example.com)" },
{ field = "username", title = "SSH 사용자명 (3/3, 예: ubuntu)" },
{ field = "name", title = "SSH 이름 (1/4, 예: prod)" },
{ field = "host", title = "SSH 주소 (2/4, 예: myserver.example.com)" },
{ field = "port", title = "SSH 포트 (3/4, 비워두면 기본값 22)", optional = true },
{ field = "username", title = "SSH 사용자명 (4/4, 예: ubuntu)" },
}
local function prompt_ssh_domain_step(window, pane, step_index, data)
@@ -289,17 +331,21 @@ local function prompt_ssh_domain_step(window, pane, step_index, data)
act.PromptInputLine({
description = step.title,
action = wezterm.action_callback(function(cb_window, cb_pane, line)
if not line or line == "" then
if (not line or line == "") and not step.optional then
notify(cb_window, "SSH 도메인 추가를 취소했습니다")
return
end
data[step.field] = line
data[step.field] = line or ""
if step_index < #ssh_domain_steps then
prompt_ssh_domain_step(cb_window, cb_pane, step_index + 1, data)
else
append_ssh_domain(cb_window, cb_pane, data.name, data.remote_address, data.username)
local remote_address = data.host
if data.port ~= "" then
remote_address = remote_address .. ":" .. data.port
end
append_ssh_domain(cb_window, cb_pane, data.name, remote_address, data.username)
end
end),
}),
@@ -311,6 +357,96 @@ local function prompt_ssh_domain(window, pane)
prompt_ssh_domain_step(window, pane, 1, {})
end
-- SSH 도메인 수정: 기존 값은 비워두면 그대로 유지, 입력하면 해당 항목만 교체
local edit_ssh_domain_steps = {
{ field = "name", label = "SSH 이름" },
{ field = "host", label = "SSH 주소" },
{ field = "port", label = "SSH 포트" },
{ field = "username", label = "SSH 사용자명" },
}
local function prompt_edit_ssh_domain_step(window, pane, step_index, data)
local step = edit_ssh_domain_steps[step_index]
local current_display = data[step.field]
if step.field == "port" and current_display == "" then
current_display = "기본값 22"
end
window:perform_action(
act.PromptInputLine({
description = string.format(
"%s (%d/%d, 현재: %s, 비워두면 유지)",
step.label,
step_index,
#edit_ssh_domain_steps,
current_display
),
action = wezterm.action_callback(function(cb_window, cb_pane, line)
if line and line ~= "" then
data[step.field] = line
end
if step_index < #edit_ssh_domain_steps then
prompt_edit_ssh_domain_step(cb_window, cb_pane, step_index + 1, data)
else
local remote_address = data.host
if data.port ~= "" then
remote_address = remote_address .. ":" .. data.port
end
update_ssh_domain(cb_window, cb_pane, data.original_name, data.name, remote_address, data.username)
end
end),
}),
pane
)
end
local function prompt_edit_ssh_domain(window, pane, domain)
local host, port = split_remote_address(domain.remote_address)
prompt_edit_ssh_domain_step(window, pane, 1, {
original_name = domain.name,
name = domain.name,
host = host,
port = port or "",
username = domain.username,
})
end
local function show_edit_ssh_domain_list(window, pane)
local choices = {}
for _, domain in ipairs(config.ssh_domains or {}) do
table.insert(choices, {
label = domain.name .. " (" .. domain.remote_address .. ")",
id = domain.name,
})
end
if #choices == 0 then
notify(window, "등록된 SSH 도메인이 없습니다")
return
end
window:perform_action(
act.InputSelector({
title = "SSH 도메인 수정",
fuzzy = true,
choices = choices,
action = wezterm.action_callback(function(inner_window, inner_pane, id)
if not id then
return
end
for _, domain in ipairs(config.ssh_domains or {}) do
if domain.name == id then
prompt_edit_ssh_domain(inner_window, inner_pane, domain)
return
end
end
end),
}),
pane
)
end
local function show_connection_settings(window, pane)
window:perform_action(
act.InputSelector({
@@ -318,12 +454,15 @@ local function show_connection_settings(window, pane)
fuzzy = true,
choices = {
{ label = "SSH 도메인 추가", id = "add_ssh" },
{ label = "SSH 도메인 수정", id = "edit_ssh" },
{ label = "SSH 도메인 목록", id = "domains" },
{ label = "통합 런처", id = "launcher" },
},
action = wezterm.action_callback(function(inner_window, inner_pane, id)
if id == "add_ssh" then
prompt_ssh_domain(inner_window, inner_pane)
elseif id == "edit_ssh" then
show_edit_ssh_domain_list(inner_window, inner_pane)
elseif id == "domains" then
inner_window:perform_action(act.ShowLauncherArgs({ flags = "DOMAINS" }), inner_pane)
elseif id == "launcher" then