The real bug was the invalid initial_value field (fixed previously), not Enter bleed-through from InputSelector. The defer-via-status-tick workaround left a gap (up to the status update interval) between the InputSelector closing and the PromptInputLine actually capturing keyboard input -- during that gap the underlying shell was visible (the reported flash) and any keystrokes typed during it went to the shell instead of the field. This is especially damaging for the 3-step SSH domain flow, where hitting the gap on any step submits an empty line and the whole entry is silently cancelled without saving. Calling perform_action synchronously, as before the call_after/defer detour, removes the gap entirely. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
580 lines
22 KiB
Lua
580 lines
22 KiB
Lua
local wezterm = require("wezterm")
|
|
local config = wezterm.config_builder() -- 구버전 wezterm이면 config = {} 로 바꾸세요
|
|
local act = wezterm.action
|
|
|
|
-- ============================================================
|
|
-- 폰트 & 테마
|
|
-- ============================================================
|
|
config.font = wezterm.font_with_fallback({
|
|
"JetBrains Mono", -- 원하는 폰트로 교체 (Nerd Font 계열 추천: JetBrainsMono Nerd Font 등)
|
|
"Noto Sans Mono CJK KR", -- 한글 fallback
|
|
})
|
|
config.font_size = 13.0
|
|
config.line_height = 1.1
|
|
|
|
config.color_scheme = "Catppuccin Mocha" -- wezterm show-config 나 https://wezterm.org/colorschemes/ 에서 이름 확인
|
|
-- 색상표를 직접 지정하고 싶으면 color_scheme 대신 아래처럼:
|
|
-- config.colors = {
|
|
-- foreground = "#c0caf5",
|
|
-- background = "#1a1b26",
|
|
-- cursor_bg = "#c0caf5",
|
|
-- cursor_border = "#c0caf5",
|
|
-- }
|
|
|
|
config.window_background_opacity = 0.92 -- 1.0이면 완전 불투명
|
|
config.macos_window_background_blur = 20 -- macOS 전용, 배경 블러
|
|
config.text_background_opacity = 1.0
|
|
|
|
-- ============================================================
|
|
-- 창 & 탭바 레이아웃
|
|
-- ============================================================
|
|
config.window_padding = {
|
|
left = 8,
|
|
right = 8,
|
|
top = 8,
|
|
bottom = 8,
|
|
}
|
|
config.initial_cols = 140
|
|
config.initial_rows = 40
|
|
|
|
config.use_fancy_tab_bar = false -- true: OS 스타일 탭바 / false: 레트로(경량) 탭바
|
|
config.tab_bar_at_bottom = false -- 탭바/상태바를 터미널 상단에 표시
|
|
config.hide_tab_bar_if_only_one_tab = false -- 탭이 하나뿐이어도 탭바를 항상 표시
|
|
config.show_new_tab_button_in_tab_bar = true
|
|
config.window_decorations = "TITLE | RESIZE" -- 상단 타이틀바 + 리사이즈 테두리 표시
|
|
|
|
-- ------------------------------------------------------------
|
|
-- [MobaXterm 스타일] 호스트(도메인)별 탭 색상 자동 구분
|
|
-- 같은 SSH 호스트/도메인에서 연 탭은 항상 같은 색으로 표시됨
|
|
-- ------------------------------------------------------------
|
|
local host_colors = {
|
|
"#f38ba8", "#fab387", "#f9e2af", "#a6e3a1",
|
|
"#94e2d5", "#89b4fa", "#cba6f7", "#f5c2e7",
|
|
}
|
|
|
|
local function color_for_host(name)
|
|
local hash = 0
|
|
for i = 1, #name do
|
|
hash = (hash * 31 + string.byte(name, i)) % 100000
|
|
end
|
|
return host_colors[(hash % #host_colors) + 1]
|
|
end
|
|
|
|
-- ------------------------------------------------------------
|
|
-- [1] 탭 제목 커스터마이징
|
|
-- 탭에 "인덱스: 실행중인 프로세스명 [호스트]" 형식으로 표시
|
|
-- local(로컬 셸)이면 회색조, SSH 도메인이면 호스트별 고유 색
|
|
-- ------------------------------------------------------------
|
|
wezterm.on("format-tab-title", function(tab, tabs, panes, config, hover, max_width)
|
|
local process = tab.active_pane.foreground_process_name or ""
|
|
local name = process:match("([^/\\]+)$") or process
|
|
if name == "" then
|
|
name = tab.active_pane.title
|
|
end
|
|
local domain = tab.active_pane.domain_name or "local"
|
|
local title = string.format(" %d: %s [%s] ", tab.tab_index + 1, name, domain)
|
|
|
|
local accent = domain == "local" and "#6c7086" or color_for_host(domain)
|
|
if tab.is_active then
|
|
return {
|
|
{ Background = { Color = accent } },
|
|
{ Foreground = { Color = "#1e1e2e" } },
|
|
{ Text = title },
|
|
}
|
|
end
|
|
return {
|
|
{ Foreground = { Color = accent } },
|
|
{ Text = title },
|
|
}
|
|
end)
|
|
|
|
-- ------------------------------------------------------------
|
|
-- [MobaXterm 스타일] 우측 상태바: 접속 호스트 + 워크스페이스 + 접속 경과시간 + 날짜/시간
|
|
-- MobaXterm 하단 상태바처럼 "지금 어디에 붙어있는지"를 한눈에 표시
|
|
-- ------------------------------------------------------------
|
|
local pane_start_times = {}
|
|
|
|
wezterm.on("update-right-status", function(window, pane)
|
|
window:set_left_status(wezterm.format({
|
|
{ Background = { Color = "#89b4fa" } },
|
|
{ Foreground = { Color = "#1e1e2e" } },
|
|
{ Text = " ☰ Menu " },
|
|
{ Background = { Color = "#313244" } },
|
|
{ Foreground = { Color = "#cdd6f4" } },
|
|
{ Text = " Alt+Click / Ctrl+a m " },
|
|
}))
|
|
|
|
local pane_id = pane:pane_id()
|
|
if not pane_start_times[pane_id] then
|
|
pane_start_times[pane_id] = os.time()
|
|
end
|
|
local elapsed = os.time() - pane_start_times[pane_id]
|
|
local elapsed_str =
|
|
string.format("%02d:%02d:%02d", elapsed // 3600, (elapsed % 3600) // 60, elapsed % 60)
|
|
|
|
local domain = pane:get_domain_name() or "local"
|
|
local workspace = window:active_workspace()
|
|
local date = wezterm.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
window:set_right_status(wezterm.format({
|
|
{ Foreground = { Color = color_for_host(domain) } },
|
|
{ Text = " " .. domain .. " " },
|
|
{ Foreground = { Color = "#89b4fa" } },
|
|
{ Text = "| " .. workspace .. " " },
|
|
{ Foreground = { Color = "#f9e2af" } },
|
|
{ Text = "| " .. elapsed_str .. " " },
|
|
{ Foreground = { Color = "#cdd6f4" } },
|
|
{ Text = "| " .. date .. " " },
|
|
}))
|
|
end)
|
|
|
|
-- ============================================================
|
|
-- [2] 세션 / 멀티플렉싱
|
|
-- ============================================================
|
|
-- SSH 도메인: 자주 접속하는 원격 서버를 wezterm이 네이티브로 관리
|
|
-- 실제 서버 정보가 준비되면 아래 예시를 복사해서 채우세요.
|
|
config.ssh_domains = {
|
|
-- {
|
|
-- name = "myserver",
|
|
-- remote_address = "myserver.example.com",
|
|
-- username = "youruser",
|
|
-- },
|
|
}
|
|
|
|
-- Unix domain (mux server): wezterm을 서버로 띄워두면 터미널 껐다 켜도 세션 유지
|
|
-- 사용법: 먼저 `wezterm-mux-server --daemonize` 실행 후, 클라이언트에서 아래 도메인에 접속
|
|
config.unix_domains = {
|
|
{ name = "unix" },
|
|
}
|
|
|
|
local is_windows = wezterm.target_triple:find("windows") ~= nil
|
|
local config_file = wezterm.config_file or (is_windows and (wezterm.home_dir .. "\\.wezterm.lua") or "/mnt/storage/wezterm.lua")
|
|
|
|
local function notify(window, message)
|
|
if window.toast_notification then
|
|
window:toast_notification("WezTerm", message, nil, 4000)
|
|
else
|
|
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.PromptInputLine({
|
|
description = title .. " (현재: " .. tostring(initial_value) .. ")",
|
|
action = wezterm.action_callback(function(cb_window, cb_pane, line)
|
|
if not line or line == "" then
|
|
return
|
|
end
|
|
local value = quote_string and string.format("%q", line) or line
|
|
set_config_value(cb_window, cb_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("\n}%s*\n\n%-%- Unix domain", "\n" .. entry .. "}\n\n-- Unix domain", 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 .. " -> " .. remote_address .. " (" .. config_file .. ")")
|
|
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)" },
|
|
}
|
|
|
|
local function prompt_ssh_domain_step(window, pane, step_index, data)
|
|
local step = ssh_domain_steps[step_index]
|
|
|
|
window:perform_action(
|
|
act.PromptInputLine({
|
|
description = step.title,
|
|
action = wezterm.action_callback(function(cb_window, cb_pane, line)
|
|
if not line or line == "" then
|
|
notify(cb_window, "SSH 도메인 추가를 취소했습니다")
|
|
return
|
|
end
|
|
|
|
data[step.field] = line
|
|
|
|
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)
|
|
end
|
|
end),
|
|
}),
|
|
pane
|
|
)
|
|
end
|
|
|
|
local function prompt_ssh_domain(window, pane)
|
|
prompt_ssh_domain_step(window, pane, 1, {})
|
|
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
|
|
|
|
local function show_main_menu(window, pane)
|
|
window:perform_action(
|
|
act.InputSelector({
|
|
title = "메인 메뉴",
|
|
fuzzy = true,
|
|
choices = {
|
|
{ label = "폰트 설정", id = "font" },
|
|
{ label = "연결 설정", id = "connection" },
|
|
{ label = "기타 설정", id = "other" },
|
|
{ label = "통합 런처", id = "launcher" },
|
|
{ label = "설정 다시 불러오기", id = "reload" },
|
|
},
|
|
action = wezterm.action_callback(function(inner_window, inner_pane, id)
|
|
if not id then
|
|
return
|
|
end
|
|
|
|
if id == "font" then
|
|
show_font_settings(inner_window, inner_pane)
|
|
elseif id == "connection" then
|
|
show_connection_settings(inner_window, inner_pane)
|
|
elseif id == "other" then
|
|
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" }),
|
|
inner_pane
|
|
)
|
|
elseif id == "reload" then
|
|
inner_window:perform_action(act.ReloadConfiguration, inner_pane)
|
|
end
|
|
end),
|
|
}),
|
|
pane
|
|
)
|
|
end
|
|
|
|
wezterm.on("show-main-menu", function(window, pane)
|
|
show_main_menu(window, pane)
|
|
end)
|
|
|
|
|
|
-- 시작 시 첫 화면으로 메인 메뉴를 띄워 설정과 연결 메뉴에 바로 접근합니다.
|
|
wezterm.on("gui-startup", function(cmd)
|
|
local tab, pane, window = wezterm.mux.spawn_window(cmd or {})
|
|
show_main_menu(window:gui_window(), pane)
|
|
end)
|
|
|
|
-- 시작 시 자동으로 워크스페이스/레이아웃을 구성하려면 아래 예시를 참고하세요.
|
|
-- wezterm.on("gui-startup", function(cmd)
|
|
-- local tab, pane, window = wezterm.mux.spawn_window(cmd or {})
|
|
-- pane:split({ direction = "Right", size = 0.3 })
|
|
-- end)
|
|
|
|
-- 워크스페이스 전환 (tmux 세션 개념) : leader+w 로 목록에서 선택/생성
|
|
-- (아래 keys 테이블에 SwitchToWorkspace, ShowLauncherArgs 바인딩 있음)
|
|
|
|
-- ============================================================
|
|
-- 키바인딩 (Leader key: tmux 스타일)
|
|
-- ============================================================
|
|
config.leader = { key = "a", mods = "CTRL", timeout_milliseconds = 1000 }
|
|
|
|
config.keys = {
|
|
-- 탭 (Windows 관례: Ctrl+Shift+*, 셸 시그널/기존 단축키와 충돌 피함)
|
|
{ key = "t", mods = "CTRL|SHIFT", action = act.SpawnTab("CurrentPaneDomain") },
|
|
{ key = "w", mods = "CTRL|SHIFT", action = act.CloseCurrentTab({ confirm = true }) },
|
|
{ key = "1", mods = "CTRL|SHIFT", action = act.ActivateTab(0) },
|
|
{ key = "2", mods = "CTRL|SHIFT", action = act.ActivateTab(1) },
|
|
{ key = "3", mods = "CTRL|SHIFT", action = act.ActivateTab(2) },
|
|
|
|
-- 창 분할 (leader + | / -)
|
|
{ key = "\\", mods = "LEADER", action = act.SplitHorizontal({ domain = "CurrentPaneDomain" }) },
|
|
{ key = "-", mods = "LEADER", action = act.SplitVertical({ domain = "CurrentPaneDomain" }) },
|
|
|
|
-- 패인(pane) 이동: leader + hjkl
|
|
{ key = "h", mods = "LEADER", action = act.ActivatePaneDirection("Left") },
|
|
{ key = "l", mods = "LEADER", action = act.ActivatePaneDirection("Right") },
|
|
{ key = "k", mods = "LEADER", action = act.ActivatePaneDirection("Up") },
|
|
{ key = "j", mods = "LEADER", action = act.ActivatePaneDirection("Down") },
|
|
|
|
-- 패인 닫기 / 전체화면 토글
|
|
{ key = "x", mods = "LEADER", action = act.CloseCurrentPane({ confirm = true }) },
|
|
{ key = "z", mods = "LEADER", action = act.TogglePaneZoomState },
|
|
|
|
-- 폰트 크기 조절 (브라우저와 동일한 관례: Ctrl+=/-/0)
|
|
{ key = "=", mods = "CTRL", action = act.IncreaseFontSize },
|
|
{ key = "-", mods = "CTRL", action = act.DecreaseFontSize },
|
|
{ key = "0", mods = "CTRL", action = act.ResetFontSize },
|
|
|
|
-- 복사/붙여넣기 (Ctrl+C/V는 셸 시그널/붙여넣기와 겹치므로 Shift 추가)
|
|
{ key = "c", mods = "CTRL|SHIFT", action = act.CopyTo("Clipboard") },
|
|
{ key = "v", mods = "CTRL|SHIFT", action = act.PasteFrom("Clipboard") },
|
|
|
|
-- 메인 메뉴: 설정 메뉴와 통합 런처를 다시 표시
|
|
{ key = "m", mods = "LEADER", action = act.EmitEvent("show-main-menu") },
|
|
|
|
-- [2] 워크스페이스(=tmux 세션) 전환/생성 목록
|
|
{ key = "w", mods = "LEADER", action = act.ShowLauncherArgs({ flags = "WORKSPACES" }) },
|
|
-- [2] SSH 도메인 접속 (config.ssh_domains에 등록한 서버 목록에서 선택)
|
|
{ key = "s", mods = "LEADER", action = act.ShowLauncherArgs({ flags = "DOMAINS" }) },
|
|
-- [MobaXterm 스타일] 저장된 세션 바로가기 목록 (config.launch_menu, 퍼지 검색 가능)
|
|
{ key = "e", mods = "LEADER", action = act.ShowLauncherArgs({ flags = "FUZZY|LAUNCH_MENU_ITEMS" }) },
|
|
-- [MobaXterm 스타일] 탭/도메인/워크스페이스/launch_menu 전부 합친 통합 런처
|
|
{ key = "Tab", mods = "LEADER", action = act.ShowLauncherArgs({ flags = "FUZZY|TABS|DOMAINS|WORKSPACES|LAUNCH_MENU_ITEMS" }) },
|
|
|
|
-- [3] 검색 모드: leader+/ 로 스크롤백 문자열 검색
|
|
{ key = "/", mods = "LEADER", action = act.Search({ CaseInSensitiveString = "" }) },
|
|
-- [3] Copy mode: leader+[ 로 진입, vim 키(hjkl/v/y)로 스크롤백 탐색 & 선택
|
|
{ key = "[", mods = "LEADER", action = act.ActivateCopyMode },
|
|
-- [3] Quick select: leader+space 로 화면에 보이는 URL/해시/경로 등을 정규식으로 잡아 즉시 복사
|
|
{ key = "Space", mods = "LEADER", action = act.QuickSelect },
|
|
|
|
-- [6] Resize 모드 진입: leader+r 누른 뒤 모드 안에서 hjkl로 계속 크기 조절, Esc로 빠져나감
|
|
{ key = "r", mods = "LEADER", action = act.ActivateKeyTable({ name = "resize_pane", one_shot = false }) },
|
|
}
|
|
|
|
-- [6] key_tables: resize 모드처럼 진입 후 여러 번 연속 입력 가능한 모달 키바인딩
|
|
config.key_tables = {
|
|
resize_pane = {
|
|
{ key = "h", action = act.AdjustPaneSize({ "Left", 3 }) },
|
|
{ key = "l", action = act.AdjustPaneSize({ "Right", 3 }) },
|
|
{ key = "k", action = act.AdjustPaneSize({ "Up", 3 }) },
|
|
{ key = "j", action = act.AdjustPaneSize({ "Down", 3 }) },
|
|
{ key = "Escape", action = "PopKeyTable" },
|
|
{ key = "q", action = "PopKeyTable" },
|
|
},
|
|
}
|
|
|
|
-- [6] 마우스 바인딩: Ctrl+Shift+우클릭으로 붙여넣기, Ctrl+클릭으로 링크 열기
|
|
config.mouse_bindings = {
|
|
{
|
|
event = { Down = { streak = 1, button = "Right" } },
|
|
mods = "CTRL|SHIFT",
|
|
action = act.PasteFrom("Clipboard"),
|
|
},
|
|
{
|
|
event = { Up = { streak = 1, button = "Left" } },
|
|
mods = "ALT",
|
|
action = act.EmitEvent("show-main-menu"),
|
|
},
|
|
{
|
|
event = { Up = { streak = 1, button = "Left" } },
|
|
mods = "CTRL",
|
|
action = act.OpenLinkAtMouseCursor,
|
|
},
|
|
}
|
|
|
|
-- [3] Quick select 패턴 추가 (기본 URL/git hash 외에 IP주소, K8s pod명 등 추가 가능)
|
|
config.quick_select_patterns = {
|
|
"\\b(?:25[0-5]|2[0-4][0-9]|1?[0-9]?[0-9])\\.(?:25[0-5]|2[0-4][0-9]|1?[0-9]?[0-9])\\.(?:25[0-5]|2[0-4][0-9]|1?[0-9]?[0-9])\\.(?:25[0-5]|2[0-4][0-9]|1?[0-9]?[0-9])\\b", -- IPv4
|
|
}
|
|
|
|
-- ============================================================
|
|
-- [4] 하이퍼링크 / 이미지
|
|
-- ============================================================
|
|
-- 기본 URL 규칙 + 커스텀 규칙 추가
|
|
config.hyperlink_rules = wezterm.default_hyperlink_rules()
|
|
-- JIRA-1234 같은 티켓 링크가 필요하면 실제 도메인으로 바꾼 뒤 활성화하세요.
|
|
-- table.insert(config.hyperlink_rules, {
|
|
-- regex = [[\b[A-Z]{2,}-\d+\b]],
|
|
-- format = "https://jira.example.com/browse/$0",
|
|
-- })
|
|
|
|
-- Kitty graphics / iTerm2 / Sixel 이미지 프로토콜 활성화 (chafa, catimg 등으로 터미널 이미지 출력 가능)
|
|
config.enable_kitty_graphics = true
|
|
|
|
-- ============================================================
|
|
-- [5] 쉘 연동
|
|
-- ============================================================
|
|
-- 기본 실행 쉘/프로그램 (미지정 시 OS 기본 쉘)
|
|
-- config.default_prog = { "/bin/zsh", "-l" }
|
|
|
|
-- OSC 7으로 현재 디렉토리를 추적하려면 쉘 설정(.zshrc/.bashrc)에 shell integration 스크립트 추가 필요:
|
|
-- zsh: eval "$(wezterm shell-integration --shell zsh)" (또는 wezterm 문서의 zshrc 스니펫)
|
|
-- 적용되면 새 탭/분할이 현재 pane과 같은 디렉토리에서 시작됨
|
|
config.default_cwd = wezterm.home_dir
|
|
|
|
-- [MobaXterm 스타일] 저장된 세션 목록: leader+e (또는 우클릭 메뉴)로 목록을 띄워
|
|
-- 원하는 서버/커맨드를 바로 골라 새 탭으로 실행. 항목은 필요한 만큼 추가하면 됨.
|
|
-- (아래는 예시이며 실제 서버 정보로 교체 필요)
|
|
config.launch_menu = {
|
|
{ label = "Local bash", args = { "bash", "-l" } },
|
|
{ label = "htop", args = { "htop" } },
|
|
-- { label = "SSH: production", args = { "ssh", "user@production.example.com" } },
|
|
-- { label = "SSH: devbox", args = { "ssh", "user@devbox.example.com" } },
|
|
-- { label = "SSH: root custom port", args = { "ssh", "-p", "2222", "root@host.example.com" } },
|
|
}
|
|
|
|
-- ============================================================
|
|
-- [7] 렌더링 / 성능
|
|
-- ============================================================
|
|
config.front_end = "WebGpu" -- GPU 가속 렌더러 (문제 있으면 "OpenGL" 또는 "Software"로 변경)
|
|
config.webgpu_power_preference = "HighPerformance"
|
|
config.max_fps = 120
|
|
config.animation_fps = 60
|
|
|
|
-- 리가처(ligature) 비활성화 (=> 같은 코드 기호가 하나로 합쳐지는 게 싫으면 끄기)
|
|
config.harfbuzz_features = { "calt=0", "clig=0", "liga=0" }
|
|
|
|
-- ============================================================
|
|
-- 기타
|
|
-- ============================================================
|
|
config.scrollback_lines = 10000
|
|
config.enable_scroll_bar = false
|
|
config.audible_bell = "Disabled"
|
|
config.default_cursor_style = "BlinkingBar"
|
|
config.window_close_confirmation = "AlwaysPrompt"
|
|
|
|
return config
|