/ wezterm / events / tab-title.lua
tab-title.lua
  1  local wezterm = require('wezterm')
  2  
  3  -- Inspired by https://github.com/wez/wezterm/discussions/628#discussioncomment-1874614
  4  
  5  local nf = wezterm.nerdfonts
  6  
  7  local ICON_SEMI_CIRCLE_LEFT = nf.ple_left_half_circle_thick --[[ '' ]]
  8  local ICON_SEMI_CIRCLE_RIGHT = nf.ple_right_half_circle_thick --[[ '' ]]
  9  local ICON_CIRCLE = nf.fa_circle --[[ '' ]]
 10  local ICON_ADMIN = nf.md_shield_half_full --[[ '󰞀' ]]
 11  
 12  local M = {}
 13  
 14  local __cells__ = {} -- wezterm FormatItems (ref: https://wezfurlong.org/wezterm/config/lua/wezterm/format.html)
 15  
 16  -- stylua: ignore
 17  local colors = {
 18     default   = { bg = '#45475a', fg = '#1c1b19' },
 19     is_active = { bg = '#7FB4CA', fg = '#11111b' },
 20     hover     = { bg = '#587d8c', fg = '#1c1b19' },
 21  }
 22  
 23  -- TODO: Needs work, pane titles are still clunky
 24  local _set_process_name = function(s)
 25    return string.gsub(s, '(.*[/\\])(.*)', '%2')
 26  end
 27  
 28  local _set_tab_idx = function(tabs, tab)
 29    local tab_idx = 1
 30  
 31    for i, t in ipairs(tabs) do
 32      if t.tab_id == tab.tab_id then
 33        tab_idx = i
 34        break
 35      end
 36    end
 37  
 38    return tab_idx
 39  end
 40  
 41  local _set_title = function(process_name, base_title, max_width, inset)
 42    local title
 43    inset = inset or 6
 44  
 45    if process_name:len() == 0 then
 46      title = '❓' .. process_name
 47    elseif process_name:len() < 4 or process_name == 'zsh' or process_name == 'bash' then
 48      title = '⚠️ ~ ' .. process_name
 49    else
 50      title = base_title
 51    end
 52  
 53    if title:len() > max_width - inset then
 54      local diff = title:len() - max_width + inset
 55      title = wezterm.truncate_right(title, title:len() - diff)
 56    end
 57  
 58    return title
 59  end
 60  
 61  -- TODO: Needs further work
 62  local _check_if_admin = function(p)
 63    if p:match('^Administrator: ') then
 64      return true
 65    end
 66    return false
 67  end
 68  
 69  ---@param fg string
 70  ---@param bg string
 71  ---@param attribute table
 72  ---@param text string
 73  local _push = function(bg, fg, attribute, text)
 74    table.insert(__cells__, { Background = { Color = bg } })
 75    table.insert(__cells__, { Foreground = { Color = fg } })
 76    table.insert(__cells__, { Attribute = attribute })
 77    table.insert(__cells__, { Text = text })
 78  end
 79  
 80  M.setup = function()
 81    wezterm.on('format-tab-title', function(tab, tabs, _, _, hover, max_width)
 82      __cells__ = {}
 83  
 84      local bg
 85      local fg
 86      local process_name = _set_process_name(tab.active_pane.foreground_process_name)
 87      local is_admin = _check_if_admin(tab.active_pane.title)
 88      local title = _set_title(process_name, tab.active_pane.title, max_width, (is_admin and 8))
 89  
 90      local tab_idx = _set_tab_idx(tabs, tab)
 91  
 92      if tab.is_active then
 93        bg = colors.is_active.bg
 94        fg = colors.is_active.fg
 95        title = tab_idx .. '   ' .. title
 96      elseif hover then
 97        bg = colors.hover.bg
 98        fg = colors.hover.fg
 99        title = tab_idx .. '   ' .. title
100      else
101        bg = colors.default.bg
102        fg = colors.default.fg
103        title = tab_idx .. '   ' .. title
104      end
105  
106      local has_unseen_output = false
107      for _, pane in ipairs(tab.panes) do
108        if pane.has_unseen_output then
109          has_unseen_output = true
110          break
111        end
112      end
113  
114      -- Left semi-circle
115      _push('rgba(0, 0, 0, 0.4)', bg, { Intensity = 'Bold' }, ICON_SEMI_CIRCLE_LEFT)
116  
117      --
118      -- Admin Icon
119      if is_admin then
120        _push(bg, fg, { Intensity = 'Bold' }, ' ' .. ICON_ADMIN)
121      end
122  
123      -- Title
124      _push(bg, fg, { Intensity = 'Bold' }, ' ' .. title)
125  
126      -- Unseen output alert
127      if has_unseen_output then
128        _push(bg, '#FFA066', { Intensity = 'Bold' }, ' ' .. ICON_CIRCLE)
129      end
130  
131      -- Right padding
132      _push(bg, fg, { Intensity = 'Bold' }, ' ')
133  
134      -- Right semi-circle
135      _push('rgba(0, 0, 0, 0.4)', bg, { Intensity = 'Bold' }, ICON_SEMI_CIRCLE_RIGHT)
136  
137      return __cells__
138    end)
139  end
140  
141  return M