From cc32af250b5c1236e446071fc235324bdcff8e4e Mon Sep 17 00:00:00 2001 From: mcrmck Date: Sun, 1 Feb 2026 11:00:06 -0500 Subject: [PATCH] =?UTF-8?q?Rename=20pane=20=E2=86=92=20window=20title=20ba?= =?UTF-8?q?r=20per=20reviewer=20feedback?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename all options from pane_title_* to window_title_* - Use foreground/background instead of fg/bg in color option names - Change color options to to_color_or_none defaulting to None, falling back to corresponding tab bar colors - Add bell_symbol, activity_symbol, progress_percent template vars using existing bell_on_tab and tab_activity_symbol options - Add custom script support via window_title_bar.py in config dir (draw_window_title function exposed as {custom} in templates) - Update C structs, Python references, and regenerate config files Co-Authored-By: Claude Opus 4.5 --- kitty/child-monitor.c | 18 +- kitty/fast_data_types.pyi | 2 +- kitty/layout/base.py | 6 +- kitty/options/definition.py | 60 +- kitty/options/parse.py | 70 +-- kitty/options/to-c-generated.h | 60 ++ kitty/options/types.py | 42 +- kitty/state.c | 14 +- kitty/state.h | 5 +- kitty/tabs.py | 8 +- ...{pane_title_bar.py => window_title_bar.py} | 146 +++-- tools/cmd/at/set_colors.go | 22 +- tools/themes/collection.go | 576 +++++++++--------- 13 files changed, 593 insertions(+), 436 deletions(-) rename kitty/{pane_title_bar.py => window_title_bar.py} (58%) diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 6d52d970f..d1afce3bd 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -807,11 +807,11 @@ prepare_to_render_os_window(OSWindow *os_window, monotonic_t now, unsigned int * if (send_cell_data_to_gpu(WD.vao_idx, WD.screen, os_window)) needs_render = true; if (WD.screen->start_visual_bell_at != 0) needs_render = true; } - // Prepare pane title bar screen data for GPU - if (w->visible && w->pane_title_render_data.screen) { - CursorRenderInfo *cri = &w->pane_title_render_data.screen->cursor_render_info; + // Prepare window title bar screen data for GPU + if (w->visible && w->window_title_render_data.screen) { + CursorRenderInfo *cri = &w->window_title_render_data.screen->cursor_render_info; zero_at_ptr(cri); - if (send_cell_data_to_gpu(w->pane_title_render_data.vao_idx, w->pane_title_render_data.screen, os_window)) needs_render = true; + if (send_cell_data_to_gpu(w->window_title_render_data.vao_idx, w->window_title_render_data.screen, os_window)) needs_render = true; } } return needs_render || was_previously_rendered_with_layers != os_window->needs_layers; @@ -874,13 +874,13 @@ render_prepared_os_window(OSWindow *os_window, unsigned int active_window_id, co if (WD.screen->start_visual_bell_at != 0) set_maximum_wait(ANIMATION_SAMPLE_WAIT); } } - // Draw pane title bars + // Draw window title bars for (unsigned int i = 0; i < tab->num_windows; i++) { Window *w = tab->windows + i; - if (w->visible && w->pane_title_render_data.screen && - w->pane_title_render_data.geometry.right > w->pane_title_render_data.geometry.left && - w->pane_title_render_data.geometry.bottom > w->pane_title_render_data.geometry.top) { - draw_cells(&w->pane_title_render_data, os_window, i == tab->active_window, true, false, NULL); + if (w->visible && w->window_title_render_data.screen && + w->window_title_render_data.geometry.right > w->window_title_render_data.geometry.left && + w->window_title_render_data.geometry.bottom > w->window_title_render_data.geometry.top) { + draw_cells(&w->window_title_render_data, os_window, i == tab->active_window, true, false, NULL); } } setup_os_window_for_rendering(os_window, tab, active_window, false); diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index c3a0eec68..13fec807c 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -1404,7 +1404,7 @@ def set_tab_bar_render_data( pass -def set_pane_title_bar_render_data( +def set_window_title_bar_render_data( os_window_id: int, tab_id: int, window_id: int, screen: Screen, left: int, top: int, right: int, bottom: int ) -> None: diff --git a/kitty/layout/base.py b/kitty/layout/base.py index 667fdfdf9..3c7f3a72a 100644 --- a/kitty/layout/base.py +++ b/kitty/layout/base.py @@ -368,11 +368,11 @@ class Layout: self.update_visibility(all_windows) self.blank_rects = [] self.do_layout(all_windows) - self._apply_pane_title_bars(all_windows) + self._apply_window_title_bars(all_windows) - def _apply_pane_title_bars(self, all_windows: WindowList) -> None: + def _apply_window_title_bars(self, all_windows: WindowList) -> None: opts = get_options() - position = opts.pane_title_bar + position = opts.window_title_bar if position == 'none': return visible_groups = list(all_windows.iter_all_layoutable_groups(only_visible=True)) diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 409e619de..6cfed2f66 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -1467,57 +1467,71 @@ actually resize the window itself, but instead, the layout row/column/slot, whic in multiple windows getting resized. ''') -opt('pane_title_bar', 'none', +opt('window_title_bar', 'none', choices=('none', 'top', 'bottom'), long_text=''' -Show a title bar for each window/pane when there are multiple windows in a tab. +Show a title bar for each window when there are multiple windows in a tab. The title bar displays the window title and is hidden when only a single window is visible. The value controls the position of the title bar relative to the window content. Set to :code:`none` to disable. ''' ) -opt('pane_title_template', '"{title}"', +opt('window_title_template', '"{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.window}{progress_percent}{title}"', option_type='tab_title_template', long_text=''' -A template to render the pane title bar text. Uses the same template syntax as +A template to render the window title bar text. Uses the same template syntax as :opt:`tab_title_template`. Available variables include: :code:`{title}`, -:code:`{index}`, :code:`{layout_name}`, :code:`{num_windows}`, -:code:`{num_window_groups}`, :code:`{tab.active_wd}`, etc. +:code:`{bell_symbol}`, :code:`{activity_symbol}`, :code:`{progress_percent}`, +:code:`{custom}`, :code:`{fmt}`, :code:`{is_active}`. +You can also provide a custom :code:`draw_window_title(data)` function in +:file:`window_title_bar.py` in the kitty config directory, exposed as :code:`{custom}`. ''' ) -opt('active_pane_title_template', 'none', +opt('active_window_title_template', 'none', option_type='tab_title_template', long_text=''' -Template to use for the active pane title bar. If not set (the value -:code:`none`), the :opt:`pane_title_template` is used. +Template to use for the active window title bar. If not set (the value +:code:`none`), the :opt:`window_title_template` is used. ''' ) -opt('pane_title_bar_active_fg', '#000000', - option_type='to_color', - long_text='Foreground color for the active pane title bar.' +opt('window_title_bar_active_foreground', 'none', + option_type='to_color_or_none', ctype='color_or_none_as_int', + long_text=''' +Foreground color for the active window title bar. Defaults to +:opt:`active_tab_foreground` when set to :code:`none`. +''' ) -opt('pane_title_bar_active_bg', '#00ff00', - option_type='to_color', - long_text='Background color for the active pane title bar.' +opt('window_title_bar_active_background', 'none', + option_type='to_color_or_none', ctype='color_or_none_as_int', + long_text=''' +Background color for the active window title bar. Defaults to +:opt:`active_tab_background` when set to :code:`none`. +''' ) -opt('pane_title_bar_inactive_fg', '#cccccc', - option_type='to_color', - long_text='Foreground color for inactive pane title bars.' +opt('window_title_bar_inactive_foreground', 'none', + option_type='to_color_or_none', ctype='color_or_none_as_int', + long_text=''' +Foreground color for inactive window title bars. Defaults to +:opt:`inactive_tab_foreground` when set to :code:`none`. +''' ) -opt('pane_title_bar_inactive_bg', '#333333', - option_type='to_color', - long_text='Background color for inactive pane title bars.' +opt('window_title_bar_inactive_background', 'none', + option_type='to_color_or_none', ctype='color_or_none_as_int', + long_text=''' +Background color for inactive window title bars. Defaults to +:opt:`inactive_tab_background` when set to :code:`none`. +''' ) -opt('pane_title_bar_align', 'center', +opt('window_title_bar_align', 'center', choices=('left', 'center', 'right'), - long_text='Horizontal alignment of the text in pane title bars.' + long_text='Horizontal alignment of the text in window title bars.' ) egr() # }}} diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 8f8249ba3..2bf282acb 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -36,9 +36,6 @@ class Parser: def active_border_color(self, val: str, ans: dict[str, typing.Any]) -> None: ans['active_border_color'] = to_color_or_none(val) - def active_pane_title_template(self, val: str, ans: dict[str, typing.Any]) -> None: - ans['active_pane_title_template'] = tab_title_template(val) - def active_tab_background(self, val: str, ans: dict[str, typing.Any]) -> None: ans['active_tab_background'] = to_color(val) @@ -51,6 +48,9 @@ class Parser: def active_tab_title_template(self, val: str, ans: dict[str, typing.Any]) -> None: ans['active_tab_title_template'] = active_tab_title_template(val) + def active_window_title_template(self, val: str, ans: dict[str, typing.Any]) -> None: + ans['active_window_title_template'] = tab_title_template(val) + def allow_cloning(self, val: str, ans: dict[str, typing.Any]) -> None: val = val.lower() if val not in self.choices_for_allow_cloning: @@ -1168,37 +1168,6 @@ class Parser: def open_url_with(self, val: str, ans: dict[str, typing.Any]) -> None: ans['open_url_with'] = to_cmdline(val) - def pane_title_bar(self, val: str, ans: dict[str, typing.Any]) -> None: - val = val.lower() - if val not in self.choices_for_pane_title_bar: - raise ValueError(f"The value {val} is not a valid choice for pane_title_bar") - ans["pane_title_bar"] = val - - choices_for_pane_title_bar = frozenset(('none', 'top', 'bottom')) - - def pane_title_bar_active_bg(self, val: str, ans: dict[str, typing.Any]) -> None: - ans['pane_title_bar_active_bg'] = to_color(val) - - def pane_title_bar_active_fg(self, val: str, ans: dict[str, typing.Any]) -> None: - ans['pane_title_bar_active_fg'] = to_color(val) - - def pane_title_bar_align(self, val: str, ans: dict[str, typing.Any]) -> None: - val = val.lower() - if val not in self.choices_for_pane_title_bar_align: - raise ValueError(f"The value {val} is not a valid choice for pane_title_bar_align") - ans["pane_title_bar_align"] = val - - choices_for_pane_title_bar_align = frozenset(('left', 'center', 'right')) - - def pane_title_bar_inactive_bg(self, val: str, ans: dict[str, typing.Any]) -> None: - ans['pane_title_bar_inactive_bg'] = to_color(val) - - def pane_title_bar_inactive_fg(self, val: str, ans: dict[str, typing.Any]) -> None: - ans['pane_title_bar_inactive_fg'] = to_color(val) - - def pane_title_template(self, val: str, ans: dict[str, typing.Any]) -> None: - ans['pane_title_template'] = tab_title_template(val) - def paste_actions(self, val: str, ans: dict[str, typing.Any]) -> None: ans['paste_actions'] = paste_actions(val) @@ -1356,7 +1325,7 @@ class Parser: raise ValueError(f"The value {val} is not a valid choice for tab_bar_align") ans["tab_bar_align"] = val - choices_for_tab_bar_align = choices_for_pane_title_bar_align + choices_for_tab_bar_align = frozenset(('left', 'center', 'right')) def tab_bar_background(self, val: str, ans: dict[str, typing.Any]) -> None: ans['tab_bar_background'] = to_color_or_none(val) @@ -1538,6 +1507,37 @@ class Parser: def window_resize_step_lines(self, val: str, ans: dict[str, typing.Any]) -> None: ans['window_resize_step_lines'] = positive_int(val) + def window_title_bar(self, val: str, ans: dict[str, typing.Any]) -> None: + val = val.lower() + if val not in self.choices_for_window_title_bar: + raise ValueError(f"The value {val} is not a valid choice for window_title_bar") + ans["window_title_bar"] = val + + choices_for_window_title_bar = frozenset(('none', 'top', 'bottom')) + + def window_title_bar_active_background(self, val: str, ans: dict[str, typing.Any]) -> None: + ans['window_title_bar_active_background'] = to_color_or_none(val) + + def window_title_bar_active_foreground(self, val: str, ans: dict[str, typing.Any]) -> None: + ans['window_title_bar_active_foreground'] = to_color_or_none(val) + + def window_title_bar_align(self, val: str, ans: dict[str, typing.Any]) -> None: + val = val.lower() + if val not in self.choices_for_window_title_bar_align: + raise ValueError(f"The value {val} is not a valid choice for window_title_bar_align") + ans["window_title_bar_align"] = val + + choices_for_window_title_bar_align = choices_for_tab_bar_align + + def window_title_bar_inactive_background(self, val: str, ans: dict[str, typing.Any]) -> None: + ans['window_title_bar_inactive_background'] = to_color_or_none(val) + + def window_title_bar_inactive_foreground(self, val: str, ans: dict[str, typing.Any]) -> None: + ans['window_title_bar_inactive_foreground'] = to_color_or_none(val) + + def window_title_template(self, val: str, ans: dict[str, typing.Any]) -> None: + ans['window_title_template'] = tab_title_template(val) + def x11_hide_window_decorations(self, val: str, ans: dict[str, typing.Any]) -> None: deprecated_hide_window_decorations_aliases('x11_hide_window_decorations', val, ans) diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index 72d5637fa..5a36c5415 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -993,6 +993,58 @@ convert_from_opts_window_drag_tolerance(PyObject *py_opts, Options *opts) { Py_DECREF(ret); } +static void +convert_from_python_window_title_bar_active_foreground(PyObject *val, Options *opts) { + opts->window_title_bar_active_foreground = color_or_none_as_int(val); +} + +static void +convert_from_opts_window_title_bar_active_foreground(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "window_title_bar_active_foreground"); + if (ret == NULL) return; + convert_from_python_window_title_bar_active_foreground(ret, opts); + Py_DECREF(ret); +} + +static void +convert_from_python_window_title_bar_active_background(PyObject *val, Options *opts) { + opts->window_title_bar_active_background = color_or_none_as_int(val); +} + +static void +convert_from_opts_window_title_bar_active_background(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "window_title_bar_active_background"); + if (ret == NULL) return; + convert_from_python_window_title_bar_active_background(ret, opts); + Py_DECREF(ret); +} + +static void +convert_from_python_window_title_bar_inactive_foreground(PyObject *val, Options *opts) { + opts->window_title_bar_inactive_foreground = color_or_none_as_int(val); +} + +static void +convert_from_opts_window_title_bar_inactive_foreground(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "window_title_bar_inactive_foreground"); + if (ret == NULL) return; + convert_from_python_window_title_bar_inactive_foreground(ret, opts); + Py_DECREF(ret); +} + +static void +convert_from_python_window_title_bar_inactive_background(PyObject *val, Options *opts) { + opts->window_title_bar_inactive_background = color_or_none_as_int(val); +} + +static void +convert_from_opts_window_title_bar_inactive_background(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "window_title_bar_inactive_background"); + if (ret == NULL) return; + convert_from_python_window_title_bar_inactive_background(ret, opts); + Py_DECREF(ret); +} + static void convert_from_python_tab_bar_edge(PyObject *val, Options *opts) { opts->tab_bar_edge = PyLong_AsLong(val); @@ -1550,6 +1602,14 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) { if (PyErr_Occurred()) return false; convert_from_opts_window_drag_tolerance(py_opts, opts); if (PyErr_Occurred()) return false; + convert_from_opts_window_title_bar_active_foreground(py_opts, opts); + if (PyErr_Occurred()) return false; + convert_from_opts_window_title_bar_active_background(py_opts, opts); + if (PyErr_Occurred()) return false; + convert_from_opts_window_title_bar_inactive_foreground(py_opts, opts); + if (PyErr_Occurred()) return false; + convert_from_opts_window_title_bar_inactive_background(py_opts, opts); + if (PyErr_Occurred()) return false; convert_from_opts_tab_bar_edge(py_opts, opts); if (PyErr_Occurred()) return false; convert_from_opts_tab_bar_margin_height(py_opts, opts); diff --git a/kitty/options/types.py b/kitty/options/types.py index a6711a79a..8cf5bb2d6 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -25,13 +25,11 @@ choices_for_default_pointer_shape = typing.Literal['arrow', 'beam', 'text', 'poi choices_for_linux_display_server = typing.Literal['auto', 'wayland', 'x11'] choices_for_macos_colorspace = typing.Literal['srgb', 'default', 'displayp3'] choices_for_macos_show_window_title_in = typing.Literal['all', 'menubar', 'none', 'window'] -choices_for_pane_title_bar = typing.Literal['none', 'top', 'bottom'] -choices_for_pane_title_bar_align = typing.Literal['left', 'center', 'right'] choices_for_placement_strategy = typing.Literal['top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right'] choices_for_pointer_shape_when_grabbed = choices_for_default_pointer_shape choices_for_scrollbar = typing.Literal['scrolled', 'always', 'never', 'hovered', 'scrolled-and-hovered'] choices_for_strip_trailing_spaces = typing.Literal['always', 'never', 'smart'] -choices_for_tab_bar_align = choices_for_pane_title_bar_align +choices_for_tab_bar_align = typing.Literal['left', 'center', 'right'] choices_for_tab_bar_style = typing.Literal['fade', 'hidden', 'powerline', 'separator', 'slant', 'custom'] choices_for_tab_powerline_style = typing.Literal['angled', 'round', 'slanted'] choices_for_tab_switch_strategy = typing.Literal['last', 'left', 'previous', 'right'] @@ -39,15 +37,17 @@ choices_for_terminfo_type = typing.Literal['path', 'direct', 'none'] choices_for_undercurl_style = typing.Literal['thin-sparse', 'thin-dense', 'thick-sparse', 'thick-dense'] choices_for_underline_hyperlinks = typing.Literal['hover', 'always', 'never'] choices_for_window_logo_position = choices_for_placement_strategy +choices_for_window_title_bar = typing.Literal['none', 'top', 'bottom'] +choices_for_window_title_bar_align = choices_for_tab_bar_align option_names = ( 'action_alias', 'active_border_color', - 'active_pane_title_template', 'active_tab_background', 'active_tab_font_style', 'active_tab_foreground', 'active_tab_title_template', + 'active_window_title_template', 'allow_cloning', 'allow_hyperlinks', 'allow_remote_control', @@ -408,13 +408,6 @@ option_names = ( 'narrow_symbols', 'notify_on_cmd_finish', 'open_url_with', - 'pane_title_bar', - 'pane_title_bar_active_bg', - 'pane_title_bar_active_fg', - 'pane_title_bar_align', - 'pane_title_bar_inactive_bg', - 'pane_title_bar_inactive_fg', - 'pane_title_template', 'paste_actions', 'pixel_scroll', 'placement_strategy', @@ -507,16 +500,23 @@ option_names = ( 'window_padding_width', 'window_resize_step_cells', 'window_resize_step_lines', + 'window_title_bar', + 'window_title_bar_active_background', + 'window_title_bar_active_foreground', + 'window_title_bar_align', + 'window_title_bar_inactive_background', + 'window_title_bar_inactive_foreground', + 'window_title_template', ) class Options: active_border_color: kitty.fast_data_types.Color | None = Color(0, 255, 0) - active_pane_title_template: str = 'none' active_tab_background: Color = Color(238, 238, 238) active_tab_font_style: tuple[bool, bool] = (True, True) active_tab_foreground: Color = Color(0, 0, 0) active_tab_title_template: str | None = None + active_window_title_template: str = 'none' allow_cloning: choices_for_allow_cloning = 'ask' allow_hyperlinks: int = 1 allow_remote_control: choices_for_allow_remote_control = 'no' @@ -611,13 +611,6 @@ class Options: mouse_hide_wait: MouseHideWait = MouseHideWait(hide_wait=0.0, show_wait=0.0, show_threshold=40, scroll_show=True) if is_macos else MouseHideWait(hide_wait=3.0, show_wait=0.0, show_threshold=40, scroll_show=True) notify_on_cmd_finish: NotifyOnCmdFinish = NotifyOnCmdFinish(when='never', duration=5.0, action='notify', cmdline=(), clear_on=('focus', 'next')) open_url_with: list[str] = ['default'] - pane_title_bar: choices_for_pane_title_bar = 'none' - pane_title_bar_active_bg: Color = Color(0, 255, 0) - pane_title_bar_active_fg: Color = Color(0, 0, 0) - pane_title_bar_align: choices_for_pane_title_bar_align = 'center' - pane_title_bar_inactive_bg: Color = Color(51, 51, 51) - pane_title_bar_inactive_fg: Color = Color(204, 204, 204) - pane_title_template: str = '{title}' paste_actions: frozenset[str] = frozenset({'confirm', 'quote-urls-at-prompt'}) pixel_scroll: bool = True placement_strategy: choices_for_placement_strategy = 'center' @@ -707,6 +700,13 @@ class Options: window_padding_width: FloatEdges = FloatEdges(left=0, top=0, right=0, bottom=0) window_resize_step_cells: int = 2 window_resize_step_lines: int = 2 + window_title_bar: choices_for_window_title_bar = 'none' + window_title_bar_active_background: kitty.fast_data_types.Color | None = None + window_title_bar_active_foreground: kitty.fast_data_types.Color | None = None + window_title_bar_align: choices_for_window_title_bar_align = 'center' + window_title_bar_inactive_background: kitty.fast_data_types.Color | None = None + window_title_bar_inactive_foreground: kitty.fast_data_types.Color | None = None + window_title_template: str = '{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.window}{progress_percent}{title}' action_alias: dict[str, str] = {} env: dict[str, str] = {} exe_search_path: dict[str, str] = {} @@ -1127,6 +1127,10 @@ nullable_colors = frozenset({ 'cursor_trail_color', 'visual_bell_color', 'active_border_color', + 'window_title_bar_active_foreground', + 'window_title_bar_active_background', + 'window_title_bar_inactive_foreground', + 'window_title_bar_inactive_background', 'tab_bar_background', 'tab_bar_margin_color', 'selection_foreground', diff --git a/kitty/state.c b/kitty/state.c index e37512e4c..c35c68f5d 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -255,16 +255,16 @@ add_tab(id_type os_window_id) { static void create_gpu_resources_for_window(Window *w) { w->render_data.vao_idx = create_cell_vao(); - w->pane_title_render_data.vao_idx = create_cell_vao(); + w->window_title_render_data.vao_idx = create_cell_vao(); } static void release_gpu_resources_for_window(Window *w) { if (w->render_data.vao_idx > -1) remove_vao(w->render_data.vao_idx); w->render_data.vao_idx = -1; - if (w->pane_title_render_data.vao_idx > -1) remove_vao(w->pane_title_render_data.vao_idx); - w->pane_title_render_data.vao_idx = -1; - Py_CLEAR(w->pane_title_render_data.screen); + if (w->window_title_render_data.vao_idx > -1) remove_vao(w->window_title_render_data.vao_idx); + w->window_title_render_data.vao_idx = -1; + Py_CLEAR(w->window_title_render_data.screen); } static bool @@ -859,13 +859,13 @@ PYWRAP1(set_tab_bar_render_data) { Py_RETURN_NONE; } -PYWRAP1(set_pane_title_bar_render_data) { +PYWRAP1(set_window_title_bar_render_data) { WindowGeometry g; id_type os_window_id, tab_id, window_id; Screen *screen; PA("KKKOIIII", &os_window_id, &tab_id, &window_id, &screen, &g.left, &g.top, &g.right, &g.bottom); WITH_WINDOW(os_window_id, tab_id, window_id) - init_window_render_data(&window->pane_title_render_data, g, screen); + init_window_render_data(&window->window_title_render_data, g, screen); END_WITH_WINDOW Py_RETURN_NONE; } @@ -1607,7 +1607,7 @@ static PyMethodDef module_methods[] = { MW(reorder_tabs, METH_VARARGS), MW(set_borders_rects, METH_VARARGS), MW(set_tab_bar_render_data, METH_VARARGS), - MW(set_pane_title_bar_render_data, METH_VARARGS), + MW(set_window_title_bar_render_data, METH_VARARGS), MW(set_window_render_data, METH_VARARGS), MW(set_window_padding, METH_VARARGS), MW(viewport_for_window, METH_VARARGS), diff --git a/kitty/state.h b/kitty/state.h index 05aec3b44..a0409d090 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -62,7 +62,8 @@ typedef struct Options { bool scrollback_fill_enlarged_window; char_type *select_by_word_characters; char_type *select_by_word_characters_forward; - color_type url_color, background, foreground, active_border_color, inactive_border_color, bell_border_color, tab_bar_background, tab_bar_margin_color; + color_type url_color, background, foreground, active_border_color, inactive_border_color, bell_border_color, tab_bar_background, tab_bar_margin_color, + window_title_bar_active_foreground, window_title_bar_active_background, window_title_bar_inactive_foreground, window_title_bar_inactive_background; monotonic_t repaint_delay, input_delay; bool focus_follows_mouse; unsigned int hide_window_decorations; @@ -208,7 +209,7 @@ typedef struct Window { bool visible; PyObject *title; WindowRenderData render_data; - WindowRenderData pane_title_render_data; + WindowRenderData window_title_render_data; WindowLogoRenderData window_logo; MousePosition mouse_pos; struct { diff --git a/kitty/tabs.py b/kitty/tabs.py index 217f73cd2..456f38a17 100644 --- a/kitty/tabs.py +++ b/kitty/tabs.py @@ -15,7 +15,7 @@ from gettext import gettext as _ from typing import Any, Concatenate, Deque, NamedTuple, Optional, ParamSpec, TypeVar, cast from .borders import Border, Borders -from .pane_title_bar import PaneTitleBarManager +from .window_title_bar import WindowTitleBarManager from .child import Child from .cli_stub import CLIOptions, SaveAsSessionOptions from .constants import appname @@ -171,7 +171,7 @@ class Tab: # {{{ self.name = getattr(session_tab, 'name', '') self.enabled_layouts = [x.lower() for x in getattr(session_tab, 'enabled_layouts', None) or get_options().enabled_layouts] self.borders = Borders(self.os_window_id, self.id) - self.pane_title_bar_manager = PaneTitleBarManager(self.os_window_id, self.id) + self.window_title_bar_manager = WindowTitleBarManager(self.os_window_id, self.id) self.windows: WindowList = WindowList(self) self._last_used_layout: str | None = None self._current_layout_name: str | None = None @@ -442,7 +442,7 @@ class Tab: # {{{ self.mark_tab_bar_dirty() def title_changed(self, window: Window) -> None: - self.pane_title_bar_manager.update(self.windows) + self.window_title_bar_manager.update(self.windows) if window is self.active_window: tm = self.tab_manager_ref() if tm is not None: @@ -471,7 +471,7 @@ class Tab: # {{{ current_layout=ly, tab_bar_rects=tm.tab_bar_rects, draw_window_borders=draw_borders ) - self.pane_title_bar_manager.update(self.windows) + self.window_title_bar_manager.update(self.windows) def create_layout_object(self, name: str) -> Layout: return create_layout_object_for(name, self.os_window_id, self.id) diff --git a/kitty/pane_title_bar.py b/kitty/window_title_bar.py similarity index 58% rename from kitty/pane_title_bar.py rename to kitty/window_title_bar.py index c3953af62..fcc53a4bb 100644 --- a/kitty/pane_title_bar.py +++ b/kitty/window_title_bar.py @@ -1,31 +1,34 @@ #!/usr/bin/env python # License: GPL v3 Copyright: 2024, kitty contributors +import os from functools import lru_cache from typing import Any, NamedTuple +from .constants import config_dir from .fast_data_types import ( DECAWM, Screen, cell_size_for_window, get_options, - set_pane_title_bar_render_data, + set_window_title_bar_render_data, ) +from .progress import ProgressState from .rgb import color_as_sgr, color_from_int, to_color -from .types import WindowGeometry +from .types import WindowGeometry, run_once from .utils import color_as_int, log_error, sgr_sanitizer_pat from .window_list import WindowList @lru_cache def _report_template_failure(template: str, e: str) -> None: - log_error(f'Invalid pane title template: "{template}" with error: {e}') + log_error(f'Invalid window title template: "{template}" with error: {e}') @lru_cache def _compile_template(template: str) -> Any: try: - return compile('f"""' + template + '"""', '', 'eval') + return compile('f"""' + template + '"""', '', 'eval') except Exception as e: _report_template_failure(template, str(e)) @@ -36,7 +39,13 @@ safe_builtins = { } -class PaneTitleColorFormatter: +def _resolve_color(opt_val: Any, fallback_val: Any) -> Any: + if opt_val is None: + return fallback_val + return opt_val + + +class WindowTitleColorFormatter: is_active: bool = False def __init__(self, which: str): @@ -46,12 +55,16 @@ class PaneTitleColorFormatter: q = name if q == 'default': ans = '9' - elif q == 'pane': + elif q == 'window': opts = get_options() if self.is_active: - col = color_from_int(color_as_int(opts.pane_title_bar_active_fg if self.which == '3' else opts.pane_title_bar_active_bg)) + fg_color = _resolve_color(opts.window_title_bar_active_foreground, opts.active_tab_foreground) + bg_color = _resolve_color(opts.window_title_bar_active_background, opts.active_tab_background) + col = color_from_int(color_as_int(fg_color if self.which == '3' else bg_color)) else: - col = color_from_int(color_as_int(opts.pane_title_bar_inactive_fg if self.which == '3' else opts.pane_title_bar_inactive_bg)) + fg_color = _resolve_color(opts.window_title_bar_inactive_foreground, opts.inactive_tab_foreground) + bg_color = _resolve_color(opts.window_title_bar_inactive_background, opts.inactive_tab_background) + col = color_from_int(color_as_int(fg_color if self.which == '3' else bg_color)) ans = f'8{color_as_sgr(col)}' elif q.startswith('color'): ans = f'8:5:{int(q[5:])}' @@ -65,10 +78,10 @@ class PaneTitleColorFormatter: return f'\x1b[{self.which}{ans}m' -class PaneTitleFormatter: +class WindowTitleFormatter: reset = '\x1b[0m' - fg = PaneTitleColorFormatter('3') - bg = PaneTitleColorFormatter('4') + fg = WindowTitleColorFormatter('3') + bg = WindowTitleColorFormatter('4') bold = '\x1b[1m' nobold = '\x1b[22m' italic = '\x1b[3m' @@ -86,14 +99,46 @@ def _draw_attributed_string(title: str, screen: Screen) -> None: screen.draw(title) -class PaneTitleData(NamedTuple): +class WindowTitleData(NamedTuple): title: str is_active: bool window_id: int tab_id: int + needs_attention: bool = False + has_activity_since_last_focus: bool = False -class PaneTitleBarScreen: +@run_once +def load_custom_window_title_bar_module() -> dict[str, Any]: + import runpy + import traceback + try: + return runpy.run_path(os.path.join(config_dir, 'window_title_bar.py')) + except FileNotFoundError: + return {} + except Exception as e: + traceback.print_exc() + log_error(f'Failed to load custom window_title_bar.py module with error: {e}') + return {} + + +def _get_custom_draw_result(data: WindowTitleData) -> str | None: + m = load_custom_window_title_bar_module() + func = m.get('draw_window_title') + if func is None: + return None + try: + return str(func(data)) + except Exception as e: + log_error(f'Custom draw_window_title function failed with error: {e}') + return None + + +def clear_caches() -> None: + load_custom_window_title_bar_module.clear_cached() + + +class WindowTitleBarScreen: def __init__(self, os_window_id: int, cell_width: int, cell_height: int): self.os_window_id = os_window_id self.cell_width = cell_width @@ -105,7 +150,7 @@ class PaneTitleBarScreen: self.screen.resize(1, ncells) self.geometry = geometry - def render(self, data: PaneTitleData) -> None: + def render(self, data: WindowTitleData, progress_percent: str) -> None: opts = get_options() s = self.screen s.cursor.x = 0 @@ -113,28 +158,39 @@ class PaneTitleBarScreen: is_active = data.is_active if is_active: - s.color_profile.default_fg = opts.pane_title_bar_active_fg - s.color_profile.default_bg = opts.pane_title_bar_active_bg - fg = (color_as_int(opts.pane_title_bar_active_fg) << 8) | 2 - bg = (color_as_int(opts.pane_title_bar_active_bg) << 8) | 2 + fg_color = _resolve_color(opts.window_title_bar_active_foreground, opts.active_tab_foreground) + bg_color = _resolve_color(opts.window_title_bar_active_background, opts.active_tab_background) else: - s.color_profile.default_fg = opts.pane_title_bar_inactive_fg - s.color_profile.default_bg = opts.pane_title_bar_inactive_bg - fg = (color_as_int(opts.pane_title_bar_inactive_fg) << 8) | 2 - bg = (color_as_int(opts.pane_title_bar_inactive_bg) << 8) | 2 + fg_color = _resolve_color(opts.window_title_bar_inactive_foreground, opts.inactive_tab_foreground) + bg_color = _resolve_color(opts.window_title_bar_inactive_background, opts.inactive_tab_background) + + s.color_profile.default_fg = fg_color + s.color_profile.default_bg = bg_color + fg = (color_as_int(fg_color) << 8) | 2 + bg = (color_as_int(bg_color) << 8) | 2 s.cursor.fg = fg s.cursor.bg = bg - template = opts.pane_title_template - if is_active and opts.active_pane_title_template and opts.active_pane_title_template != 'none': - template = opts.active_pane_title_template + template = opts.window_title_template + if is_active and opts.active_window_title_template and opts.active_window_title_template != 'none': + template = opts.active_window_title_template + + WindowTitleColorFormatter.is_active = is_active + + bell_symbol = opts.bell_on_tab if data.needs_attention else '' + activity_symbol = opts.tab_activity_symbol if data.has_activity_since_last_focus else '' + + custom_result = _get_custom_draw_result(data) - PaneTitleColorFormatter.is_active = is_active eval_locals = { 'title': data.title, 'is_active': is_active, - 'fmt': PaneTitleFormatter, + 'fmt': WindowTitleFormatter, + 'bell_symbol': bell_symbol, + 'activity_symbol': activity_symbol, + 'progress_percent': progress_percent, + 'custom': custom_result or '', } try: title = eval(_compile_template(template), {'__builtins__': safe_builtins}, eval_locals) @@ -143,7 +199,7 @@ class PaneTitleBarScreen: title = data.title title_str = str(title) - align = opts.pane_title_bar_align + align = opts.window_title_bar_align if align == 'left': _draw_attributed_string(title_str, s) @@ -171,17 +227,17 @@ class PaneTitleBarScreen: s.draw(' ') -class PaneTitleBarManager: +class WindowTitleBarManager: def __init__(self, os_window_id: int, tab_id: int): self.os_window_id = os_window_id self.tab_id = tab_id - self._screens: dict[int, PaneTitleBarScreen] = {} + self._screens: dict[int, WindowTitleBarScreen] = {} def _clear_all(self) -> None: for wid, pts in self._screens.items(): # Zero geometry so the C render loop skips drawing - set_pane_title_bar_render_data( + set_window_title_bar_render_data( self.os_window_id, self.tab_id, wid, pts.screen, 0, 0, 0, 0, ) @@ -189,7 +245,7 @@ class PaneTitleBarManager: def update(self, all_windows: WindowList) -> None: opts = get_options() - position = opts.pane_title_bar + position = opts.window_title_bar if position == 'none': if self._screens: self._clear_all() @@ -226,7 +282,7 @@ class PaneTitleBarManager: seen_window_ids.add(wid) if wid not in self._screens: - self._screens[wid] = PaneTitleBarScreen(self.os_window_id, cell_width, cell_height) + self._screens[wid] = WindowTitleBarScreen(self.os_window_id, cell_width, cell_height) pts = self._screens[wid] @@ -251,15 +307,33 @@ class PaneTitleBarManager: pts.layout(title_geom) is_active = wg is active_group - data = PaneTitleData( + + # Get bell/activity state from the window object + needs_attention = getattr(window, 'needs_attention', False) + has_activity = getattr(window, 'has_activity_since_last_focus', False) + if callable(has_activity): + has_activity = has_activity() + + # Get progress info + progress_percent = '' + progress = getattr(window, 'progress', None) + if progress is not None and progress.state is not ProgressState.unset: + if progress.state is ProgressState.indeterminate: + progress_percent = '[…] ' + elif progress.percent > 0: + progress_percent = f'[{progress.percent}%] ' + + data = WindowTitleData( title=window.title or '', is_active=is_active, window_id=wid, tab_id=self.tab_id, + needs_attention=needs_attention, + has_activity_since_last_focus=has_activity, ) - pts.render(data) + pts.render(data, progress_percent) - set_pane_title_bar_render_data( + set_window_title_bar_render_data( self.os_window_id, self.tab_id, wid, pts.screen, title_geom.left, title_geom.top, title_geom.right, title_geom.bottom, ) diff --git a/tools/cmd/at/set_colors.go b/tools/cmd/at/set_colors.go index 6557cfe1d..6370ada3b 100644 --- a/tools/cmd/at/set_colors.go +++ b/tools/cmd/at/set_colors.go @@ -15,15 +15,19 @@ import ( var nullable_colors = map[string]bool{ // generated by gen-config.py do not edit // NULLABLE_COLORS_START - "active_border_color": true, - "cursor": true, - "cursor_text_color": true, - "cursor_trail_color": true, - "selection_background": true, - "selection_foreground": true, - "tab_bar_background": true, - "tab_bar_margin_color": true, - "visual_bell_color": true, + "active_border_color": true, + "cursor": true, + "cursor_text_color": true, + "cursor_trail_color": true, + "selection_background": true, + "selection_foreground": true, + "tab_bar_background": true, + "tab_bar_margin_color": true, + "visual_bell_color": true, + "window_title_bar_active_background": true, + "window_title_bar_active_foreground": true, + "window_title_bar_inactive_background": true, + "window_title_bar_inactive_foreground": true, // NULLABLE_COLORS_END } diff --git a/tools/themes/collection.go b/tools/themes/collection.go index 5ba3fb4d1..355c6d77f 100644 --- a/tools/themes/collection.go +++ b/tools/themes/collection.go @@ -35,294 +35,294 @@ var _ = fmt.Print var AllColorSettingNames = map[string]bool{ // {{{ // generated by gen-config.py do not edit // ALL_COLORS_START - "active_border_color": true, - "active_tab_background": true, - "active_tab_foreground": true, - "background": true, - "bell_border_color": true, - "color0": true, - "color1": true, - "color10": true, - "color100": true, - "color101": true, - "color102": true, - "color103": true, - "color104": true, - "color105": true, - "color106": true, - "color107": true, - "color108": true, - "color109": true, - "color11": true, - "color110": true, - "color111": true, - "color112": true, - "color113": true, - "color114": true, - "color115": true, - "color116": true, - "color117": true, - "color118": true, - "color119": true, - "color12": true, - "color120": true, - "color121": true, - "color122": true, - "color123": true, - "color124": true, - "color125": true, - "color126": true, - "color127": true, - "color128": true, - "color129": true, - "color13": true, - "color130": true, - "color131": true, - "color132": true, - "color133": true, - "color134": true, - "color135": true, - "color136": true, - "color137": true, - "color138": true, - "color139": true, - "color14": true, - "color140": true, - "color141": true, - "color142": true, - "color143": true, - "color144": true, - "color145": true, - "color146": true, - "color147": true, - "color148": true, - "color149": true, - "color15": true, - "color150": true, - "color151": true, - "color152": true, - "color153": true, - "color154": true, - "color155": true, - "color156": true, - "color157": true, - "color158": true, - "color159": true, - "color16": true, - "color160": true, - "color161": true, - "color162": true, - "color163": true, - "color164": true, - "color165": true, - "color166": true, - "color167": true, - "color168": true, - "color169": true, - "color17": true, - "color170": true, - "color171": true, - "color172": true, - "color173": true, - "color174": true, - "color175": true, - "color176": true, - "color177": true, - "color178": true, - "color179": true, - "color18": true, - "color180": true, - "color181": true, - "color182": true, - "color183": true, - "color184": true, - "color185": true, - "color186": true, - "color187": true, - "color188": true, - "color189": true, - "color19": true, - "color190": true, - "color191": true, - "color192": true, - "color193": true, - "color194": true, - "color195": true, - "color196": true, - "color197": true, - "color198": true, - "color199": true, - "color2": true, - "color20": true, - "color200": true, - "color201": true, - "color202": true, - "color203": true, - "color204": true, - "color205": true, - "color206": true, - "color207": true, - "color208": true, - "color209": true, - "color21": true, - "color210": true, - "color211": true, - "color212": true, - "color213": true, - "color214": true, - "color215": true, - "color216": true, - "color217": true, - "color218": true, - "color219": true, - "color22": true, - "color220": true, - "color221": true, - "color222": true, - "color223": true, - "color224": true, - "color225": true, - "color226": true, - "color227": true, - "color228": true, - "color229": true, - "color23": true, - "color230": true, - "color231": true, - "color232": true, - "color233": true, - "color234": true, - "color235": true, - "color236": true, - "color237": true, - "color238": true, - "color239": true, - "color24": true, - "color240": true, - "color241": true, - "color242": true, - "color243": true, - "color244": true, - "color245": true, - "color246": true, - "color247": true, - "color248": true, - "color249": true, - "color25": true, - "color250": true, - "color251": true, - "color252": true, - "color253": true, - "color254": true, - "color255": true, - "color26": true, - "color27": true, - "color28": true, - "color29": true, - "color3": true, - "color30": true, - "color31": true, - "color32": true, - "color33": true, - "color34": true, - "color35": true, - "color36": true, - "color37": true, - "color38": true, - "color39": true, - "color4": true, - "color40": true, - "color41": true, - "color42": true, - "color43": true, - "color44": true, - "color45": true, - "color46": true, - "color47": true, - "color48": true, - "color49": true, - "color5": true, - "color50": true, - "color51": true, - "color52": true, - "color53": true, - "color54": true, - "color55": true, - "color56": true, - "color57": true, - "color58": true, - "color59": true, - "color6": true, - "color60": true, - "color61": true, - "color62": true, - "color63": true, - "color64": true, - "color65": true, - "color66": true, - "color67": true, - "color68": true, - "color69": true, - "color7": true, - "color70": true, - "color71": true, - "color72": true, - "color73": true, - "color74": true, - "color75": true, - "color76": true, - "color77": true, - "color78": true, - "color79": true, - "color8": true, - "color80": true, - "color81": true, - "color82": true, - "color83": true, - "color84": true, - "color85": true, - "color86": true, - "color87": true, - "color88": true, - "color89": true, - "color9": true, - "color90": true, - "color91": true, - "color92": true, - "color93": true, - "color94": true, - "color95": true, - "color96": true, - "color97": true, - "color98": true, - "color99": true, - "cursor": true, - "cursor_text_color": true, - "cursor_trail_color": true, - "foreground": true, - "inactive_border_color": true, - "inactive_tab_background": true, - "inactive_tab_foreground": true, - "macos_titlebar_color": true, - "mark1_background": true, - "mark1_foreground": true, - "mark2_background": true, - "mark2_foreground": true, - "mark3_background": true, - "mark3_foreground": true, - "pane_title_bar_active_bg": true, - "pane_title_bar_active_fg": true, - "pane_title_bar_inactive_bg": true, - "pane_title_bar_inactive_fg": true, - "scrollbar_handle_color": true, - "scrollbar_track_color": true, - "selection_background": true, - "selection_foreground": true, - "tab_bar_background": true, - "tab_bar_margin_color": true, - "url_color": true, - "visual_bell_color": true, - "wayland_titlebar_color": true, // ALL_COLORS_END + "active_border_color": true, + "active_tab_background": true, + "active_tab_foreground": true, + "background": true, + "bell_border_color": true, + "color0": true, + "color1": true, + "color10": true, + "color100": true, + "color101": true, + "color102": true, + "color103": true, + "color104": true, + "color105": true, + "color106": true, + "color107": true, + "color108": true, + "color109": true, + "color11": true, + "color110": true, + "color111": true, + "color112": true, + "color113": true, + "color114": true, + "color115": true, + "color116": true, + "color117": true, + "color118": true, + "color119": true, + "color12": true, + "color120": true, + "color121": true, + "color122": true, + "color123": true, + "color124": true, + "color125": true, + "color126": true, + "color127": true, + "color128": true, + "color129": true, + "color13": true, + "color130": true, + "color131": true, + "color132": true, + "color133": true, + "color134": true, + "color135": true, + "color136": true, + "color137": true, + "color138": true, + "color139": true, + "color14": true, + "color140": true, + "color141": true, + "color142": true, + "color143": true, + "color144": true, + "color145": true, + "color146": true, + "color147": true, + "color148": true, + "color149": true, + "color15": true, + "color150": true, + "color151": true, + "color152": true, + "color153": true, + "color154": true, + "color155": true, + "color156": true, + "color157": true, + "color158": true, + "color159": true, + "color16": true, + "color160": true, + "color161": true, + "color162": true, + "color163": true, + "color164": true, + "color165": true, + "color166": true, + "color167": true, + "color168": true, + "color169": true, + "color17": true, + "color170": true, + "color171": true, + "color172": true, + "color173": true, + "color174": true, + "color175": true, + "color176": true, + "color177": true, + "color178": true, + "color179": true, + "color18": true, + "color180": true, + "color181": true, + "color182": true, + "color183": true, + "color184": true, + "color185": true, + "color186": true, + "color187": true, + "color188": true, + "color189": true, + "color19": true, + "color190": true, + "color191": true, + "color192": true, + "color193": true, + "color194": true, + "color195": true, + "color196": true, + "color197": true, + "color198": true, + "color199": true, + "color2": true, + "color20": true, + "color200": true, + "color201": true, + "color202": true, + "color203": true, + "color204": true, + "color205": true, + "color206": true, + "color207": true, + "color208": true, + "color209": true, + "color21": true, + "color210": true, + "color211": true, + "color212": true, + "color213": true, + "color214": true, + "color215": true, + "color216": true, + "color217": true, + "color218": true, + "color219": true, + "color22": true, + "color220": true, + "color221": true, + "color222": true, + "color223": true, + "color224": true, + "color225": true, + "color226": true, + "color227": true, + "color228": true, + "color229": true, + "color23": true, + "color230": true, + "color231": true, + "color232": true, + "color233": true, + "color234": true, + "color235": true, + "color236": true, + "color237": true, + "color238": true, + "color239": true, + "color24": true, + "color240": true, + "color241": true, + "color242": true, + "color243": true, + "color244": true, + "color245": true, + "color246": true, + "color247": true, + "color248": true, + "color249": true, + "color25": true, + "color250": true, + "color251": true, + "color252": true, + "color253": true, + "color254": true, + "color255": true, + "color26": true, + "color27": true, + "color28": true, + "color29": true, + "color3": true, + "color30": true, + "color31": true, + "color32": true, + "color33": true, + "color34": true, + "color35": true, + "color36": true, + "color37": true, + "color38": true, + "color39": true, + "color4": true, + "color40": true, + "color41": true, + "color42": true, + "color43": true, + "color44": true, + "color45": true, + "color46": true, + "color47": true, + "color48": true, + "color49": true, + "color5": true, + "color50": true, + "color51": true, + "color52": true, + "color53": true, + "color54": true, + "color55": true, + "color56": true, + "color57": true, + "color58": true, + "color59": true, + "color6": true, + "color60": true, + "color61": true, + "color62": true, + "color63": true, + "color64": true, + "color65": true, + "color66": true, + "color67": true, + "color68": true, + "color69": true, + "color7": true, + "color70": true, + "color71": true, + "color72": true, + "color73": true, + "color74": true, + "color75": true, + "color76": true, + "color77": true, + "color78": true, + "color79": true, + "color8": true, + "color80": true, + "color81": true, + "color82": true, + "color83": true, + "color84": true, + "color85": true, + "color86": true, + "color87": true, + "color88": true, + "color89": true, + "color9": true, + "color90": true, + "color91": true, + "color92": true, + "color93": true, + "color94": true, + "color95": true, + "color96": true, + "color97": true, + "color98": true, + "color99": true, + "cursor": true, + "cursor_text_color": true, + "cursor_trail_color": true, + "foreground": true, + "inactive_border_color": true, + "inactive_tab_background": true, + "inactive_tab_foreground": true, + "macos_titlebar_color": true, + "mark1_background": true, + "mark1_foreground": true, + "mark2_background": true, + "mark2_foreground": true, + "mark3_background": true, + "mark3_foreground": true, + "scrollbar_handle_color": true, + "scrollbar_track_color": true, + "selection_background": true, + "selection_foreground": true, + "tab_bar_background": true, + "tab_bar_margin_color": true, + "url_color": true, + "visual_bell_color": true, + "wayland_titlebar_color": true, + "window_title_bar_active_background": true, + "window_title_bar_active_foreground": true, + "window_title_bar_inactive_background": true, + "window_title_bar_inactive_foreground": true, // ALL_COLORS_END } // }}} type JSONMetadata struct {