From 0f9632a6782ee3ec2bfd0edbf2c3420b1d9b5de3 Mon Sep 17 00:00:00 2001 From: gm Date: Fri, 17 Jul 2026 00:49:11 +0900 Subject: [PATCH] Add clickable menu and prompt settings --- wezterm.lua | 265 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 229 insertions(+), 36 deletions(-) diff --git a/wezterm.lua b/wezterm.lua index fb49926..3250dfb 100644 --- a/wezterm.lua +++ b/wezterm.lua @@ -96,12 +96,14 @@ local pane_start_times = {} wezterm.on("update-right-status", function(window, pane) window:set_left_status(wezterm.format({ + { Hyperlink = { uri = "wezterm://main-menu" } }, { Background = { Color = "#89b4fa" } }, { Foreground = { Color = "#1e1e2e" } }, - { Text = " ☰ " }, + { Text = " ☰ Menu " }, + { Hyperlink = { uri = "" } }, { Background = { Color = "#313244" } }, { Foreground = { Color = "#cdd6f4" } }, - { Text = " Menu: Ctrl+a m " }, + { Text = " Ctrl+a m " }, })) local pane_id = pane:pane_id() @@ -150,41 +152,225 @@ config.unix_domains = { local is_windows = wezterm.target_triple:find("windows") ~= nil local config_file = is_windows and (wezterm.home_dir .. "\\.wezterm.lua") or "/mnt/storage/wezterm.lua" -local function open_config_section(window, pane, line) - local args - if is_windows then - args = { - "powershell.exe", - "-NoLogo", - "-NoExit", - "-Command", - string.format( - '$file = %q; $line = %d; ' - .. '$code = Get-Command code.cmd,code -ErrorAction SilentlyContinue | Select-Object -First 1; ' - .. 'if ($code) { & $code.Source -g "${file}:${line}" } ' - .. 'else { Write-Host "VS Code not found. Opening Notepad without line jump."; notepad.exe $file }', - config_file, - line - ), - } +local function notify(window, message) + if window.toast_notification then + window:toast_notification("WezTerm", message, nil, 4000) else - args = { - "sh", - "-lc", - string.format( - 'file=%q; line=%d; editor="${VISUAL:-${EDITOR:-}}"; ' - .. 'if [ -z "$editor" ]; then for candidate in nvim vim vi nano; do command -v "$candidate" >/dev/null 2>&1 && editor="$candidate" && break; done; fi; ' - .. 'if [ -z "$editor" ]; then echo "No terminal editor found. Set EDITOR or install vim/nano."; exec "${SHELL:-sh}"; fi; ' - .. '"$editor" +"$line" "$file"; status=$?; ' - .. 'if [ $status -ne 0 ]; then echo; echo "Editor exited with status $status. Press Enter to close, or type commands below."; exec "${SHELL:-sh}"; fi', - config_file, - line - ), - } + wezterm.log_info(message) + end +end + +local function read_config_text() + local file = io.open(config_file, "r") + if not file then + return nil, "설정 파일을 열 수 없습니다: " .. config_file + end + local text = file:read("*a") + file:close() + return text +end + +local function write_config_text(text) + local file = io.open(config_file, "w") + if not file then + return false, "설정 파일에 쓸 수 없습니다: " .. config_file + end + file:write(text) + file:close() + return true +end + +local function set_config_value(window, pane, key, value) + local text, err = read_config_text() + if not text then + notify(window, err) + return end + local pattern = "(config%." .. key .. "%s*=%s*)[^\n]+" + local updated, count = text:gsub(pattern, function(prefix) + return prefix .. value + end, 1) + + if count == 0 then + notify(window, "설정 항목을 찾지 못했습니다: config." .. key) + return + end + + local ok, write_err = write_config_text(updated) + if not ok then + notify(window, write_err) + return + end + + notify(window, "저장됨: config." .. key .. " = " .. value) + window:perform_action(act.ReloadConfiguration, pane) +end + +local function prompt_config_value(window, pane, title, key, initial_value, quote_string) window:perform_action( - act.SpawnCommandInNewTab({ args = args }), + act.PromptInputLine({ + description = title, + initial_value = tostring(initial_value), + action = wezterm.action_callback(function(inner_window, inner_pane, line) + if not line or line == "" then + return + end + local value = quote_string and string.format("%q", line) or line + set_config_value(inner_window, inner_pane, key, value) + end), + }), + pane + ) +end + +local function show_font_settings(window, pane) + window:perform_action( + act.InputSelector({ + title = "폰트 설정", + fuzzy = true, + choices = { + { label = "폰트 크기", id = "font_size" }, + { label = "줄 높이", id = "line_height" }, + { label = "색상 테마", id = "color_scheme" }, + { label = "창 투명도", id = "opacity" }, + }, + action = wezterm.action_callback(function(inner_window, inner_pane, id) + if id == "font_size" then + prompt_config_value(inner_window, inner_pane, "폰트 크기", "font_size", config.font_size, false) + elseif id == "line_height" then + prompt_config_value(inner_window, inner_pane, "줄 높이", "line_height", config.line_height, false) + elseif id == "color_scheme" then + prompt_config_value(inner_window, inner_pane, "색상 테마", "color_scheme", config.color_scheme, true) + elseif id == "opacity" then + prompt_config_value(inner_window, inner_pane, "창 투명도", "window_background_opacity", config.window_background_opacity, false) + end + end), + }), + pane + ) +end + +local function append_ssh_domain(window, pane, name, remote_address, username) + local text, err = read_config_text() + if not text then + notify(window, err) + return + end + + 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 updated, count = text:gsub("(config%.ssh_domains%s*=%s*{%s*\n)", function(prefix) + return prefix .. entry + end, 1) + + if count == 0 then + notify(window, "config.ssh_domains 블록을 찾지 못했습니다") + return + end + + local ok, write_err = write_config_text(updated) + if not ok then + notify(window, write_err) + return + end + + notify(window, "SSH 도메인 추가됨: " .. name) + window:perform_action(act.ReloadConfiguration, pane) +end + +local function prompt_ssh_domain(window, pane) + local draft = {} + window:perform_action( + act.PromptInputLine({ + description = "SSH 이름", + action = wezterm.action_callback(function(w1, p1, name) + if not name or name == "" then + return + end + draft.name = name + w1:perform_action( + act.PromptInputLine({ + description = "SSH 주소", + action = wezterm.action_callback(function(w2, p2, address) + if not address or address == "" then + return + end + draft.remote_address = address + w2:perform_action( + act.PromptInputLine({ + description = "SSH 사용자명", + action = wezterm.action_callback(function(w3, p3, username) + if not username or username == "" then + return + end + append_ssh_domain(w3, p3, draft.name, draft.remote_address, username) + end), + }), + p2 + ) + end), + }), + p1 + ) + end), + }), + pane + ) +end + +local function show_connection_settings(window, pane) + window:perform_action( + act.InputSelector({ + title = "연결 설정", + fuzzy = true, + choices = { + { label = "SSH 도메인 추가", id = "add_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 == "domains" then + inner_window:perform_action(act.ShowLauncherArgs({ flags = "DOMAINS" }), inner_pane) + elseif id == "launcher" then + inner_window:perform_action(act.ShowLauncherArgs({ flags = "FUZZY|TABS|DOMAINS|WORKSPACES|LAUNCH_MENU_ITEMS" }), inner_pane) + end + end), + }), + pane + ) +end + +local function show_other_settings(window, pane) + window:perform_action( + act.InputSelector({ + title = "기타 설정", + fuzzy = true, + choices = { + { label = "스크롤백 줄 수", id = "scrollback" }, + { label = "최대 FPS", id = "max_fps" }, + { label = "탭바 위치: 상단", id = "tab_top" }, + { label = "탭바 위치: 하단", id = "tab_bottom" }, + { label = "창 닫기 확인 켜기", id = "close_prompt" }, + { label = "창 닫기 확인 끄기", id = "close_no_prompt" }, + }, + action = wezterm.action_callback(function(inner_window, inner_pane, id) + if id == "scrollback" then + prompt_config_value(inner_window, inner_pane, "스크롤백 줄 수", "scrollback_lines", config.scrollback_lines, false) + elseif id == "max_fps" then + prompt_config_value(inner_window, inner_pane, "최대 FPS", "max_fps", config.max_fps, false) + elseif id == "tab_top" then + set_config_value(inner_window, inner_pane, "tab_bar_at_bottom", "false") + elseif id == "tab_bottom" then + set_config_value(inner_window, inner_pane, "tab_bar_at_bottom", "true") + elseif id == "close_prompt" then + set_config_value(inner_window, inner_pane, "window_close_confirmation", string.format("%q", "AlwaysPrompt")) + elseif id == "close_no_prompt" then + set_config_value(inner_window, inner_pane, "window_close_confirmation", string.format("%q", "NeverPrompt")) + end + end), + }), pane ) end @@ -207,11 +393,11 @@ local function show_main_menu(window, pane) end if id == "font" then - open_config_section(inner_window, inner_pane, 6) + show_font_settings(inner_window, inner_pane) elseif id == "connection" then - open_config_section(inner_window, inner_pane, 123) + show_connection_settings(inner_window, inner_pane) elseif id == "other" then - open_config_section(inner_window, inner_pane, 339) + show_other_settings(inner_window, inner_pane) elseif id == "launcher" then inner_window:perform_action( act.ShowLauncherArgs({ flags = "FUZZY|TABS|DOMAINS|WORKSPACES|LAUNCH_MENU_ITEMS" }), @@ -230,6 +416,13 @@ wezterm.on("show-main-menu", function(window, pane) show_main_menu(window, pane) end) +wezterm.on("open-uri", function(window, pane, uri) + if uri == "wezterm://main-menu" then + show_main_menu(window, pane) + return false + end +end) + -- 시작 시 첫 화면으로 메인 메뉴를 띄워 설정과 연결 메뉴에 바로 접근합니다. wezterm.on("gui-startup", function(cmd) local tab, pane, window = wezterm.mux.spawn_window(cmd or {})