Files
wezterm-config/wezterm.lua
gm 9c36a97dd2 Remove update-right-status defer hack, call PromptInputLine directly
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>
2026-07-18 00:41:44 +09:00

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