mirror of
https://github.com/kovidgoyal/kitty
synced 2026-06-06 09:15:57 +02:00
Make cmd finish notification clear behavior configurable
This commit is contained in:
@@ -89,6 +89,8 @@ Detailed list of changes
|
||||
|
||||
- Allow :ref:`specifying individual color themes <auto_color_scheme>` to use so that kitty changes colors automatically following the OS dark/light mode
|
||||
|
||||
- :opt:`notify_on_cmd_finish`: Automatically remove notifications when the window gains focus or the next notification is shown. Clearing behavior can be configured (:pull:`8100`)
|
||||
|
||||
- Discard OSC 9 notifications that start with :code:`4;` because some misguided software is using it for "progress reporting" (:iss:`8011`)
|
||||
|
||||
- Wayland GNOME: Workaround bug in mutter causing double tap on titlebar to not always work (:iss:`8054`)
|
||||
|
||||
@@ -3304,7 +3304,13 @@ and exits will spam a notification.
|
||||
Second, the action to perform. The default is :code:`notify`. The possible values are:
|
||||
|
||||
:code:`notify`
|
||||
Send a desktop notification.
|
||||
Send a desktop notification. The subsequent arguments are optional and specify when
|
||||
the notification is automatically cleared. The set of possible events when the notification is
|
||||
cleared are: :code:`focus` and :code:`next`. :code:`focus` means that when the notification
|
||||
policy is :code:`unfocused` or :code:`invisible` the notification is automatically cleared
|
||||
when the window regains focus. The value of :code:`next` means that the previous notification
|
||||
is cleared when the next notification is shown. The default when no arguments are specified
|
||||
is: :code:`focus next`.
|
||||
|
||||
:code:`bell`
|
||||
Ring the terminal bell.
|
||||
@@ -3323,6 +3329,9 @@ Some more examples::
|
||||
# Run 'notify-send' when a command takes more than 10 seconds in a invisible window
|
||||
# Here %c is replaced by the current command line and %s by the job exit code
|
||||
notify_on_cmd_finish invisible 10.0 command notify-send "job finished with status: %s" %c
|
||||
# Do not clear previous notification when next command finishes or window regains focus
|
||||
notify_on_cmd_finish invisible 5.0 notify
|
||||
|
||||
'''
|
||||
)
|
||||
|
||||
|
||||
2
kitty/options/types.py
generated
2
kitty/options/types.py
generated
@@ -565,7 +565,7 @@ class Options:
|
||||
mark3_background: Color = Color(242, 116, 188)
|
||||
mark3_foreground: Color = Color(0, 0, 0)
|
||||
mouse_hide_wait: float = 0.0 if is_macos else 3.0
|
||||
notify_on_cmd_finish: NotifyOnCmdFinish = NotifyOnCmdFinish(when='never', duration=5.0, action='notify', cmdline=())
|
||||
notify_on_cmd_finish: NotifyOnCmdFinish = NotifyOnCmdFinish(when='never', duration=5.0, action='notify', cmdline=(), clear_on=('focus', 'next'))
|
||||
open_url_with: typing.List[str] = ['default']
|
||||
paste_actions: typing.FrozenSet[str] = frozenset({'confirm', 'quote-urls-at-prompt'})
|
||||
placement_strategy: choices_for_placement_strategy = 'center'
|
||||
|
||||
@@ -26,6 +26,7 @@ from typing import (
|
||||
Tuple,
|
||||
TypeVar,
|
||||
Union,
|
||||
cast,
|
||||
get_args,
|
||||
)
|
||||
|
||||
@@ -763,11 +764,17 @@ def active_tab_title_template(x: str) -> Optional[str]:
|
||||
return None if x == 'none' else x
|
||||
|
||||
|
||||
ClearOn = Literal['next', 'focus']
|
||||
default_clear_on: tuple[ClearOn, ...] = 'focus', 'next'
|
||||
all_clear_on = get_args(ClearOn)
|
||||
|
||||
|
||||
class NotifyOnCmdFinish(NamedTuple):
|
||||
when: str
|
||||
duration: float
|
||||
action: str
|
||||
cmdline: Tuple[str, ...]
|
||||
when: str = 'never'
|
||||
duration: float = 5.0
|
||||
action: str = 'notify'
|
||||
cmdline: Tuple[str, ...] = ()
|
||||
clear_on: tuple[ClearOn, ...] = default_clear_on
|
||||
|
||||
|
||||
def notify_on_cmd_finish(x: str) -> NotifyOnCmdFinish:
|
||||
@@ -780,16 +787,26 @@ def notify_on_cmd_finish(x: str) -> NotifyOnCmdFinish:
|
||||
duration = float(parts[1])
|
||||
action = 'notify'
|
||||
cmdline: Tuple[str, ...] = ()
|
||||
clear_on = default_clear_on
|
||||
if len(parts) > 2:
|
||||
if parts[2] not in ('notify', 'bell', 'command'):
|
||||
raise ValueError(f'Unknown notify_on_cmd_finish action: {parts[2]}')
|
||||
action = parts[2]
|
||||
if action == 'command':
|
||||
if action == 'notify':
|
||||
if len(parts) > 3:
|
||||
con: list[ClearOn] = []
|
||||
for x in parts[3].split():
|
||||
if x not in all_clear_on:
|
||||
raise ValueError(
|
||||
f'notify_on_cmd_finish: notify clear_on value "{x}" is invalid. Valid values are: {", ".join(all_clear_on)}')
|
||||
con.append(cast(ClearOn, x))
|
||||
clear_on = tuple(con)
|
||||
elif action == 'command':
|
||||
if len(parts) > 3:
|
||||
cmdline = tuple(to_cmdline(parts[3]))
|
||||
else:
|
||||
raise ValueError('notify_on_cmd_finish `command` action needs a command line')
|
||||
return NotifyOnCmdFinish(when, duration, action, cmdline)
|
||||
return NotifyOnCmdFinish(when, duration, action, cmdline, clear_on)
|
||||
|
||||
|
||||
def config_or_absolute_path(x: str, env: Optional[Dict[str, str]] = None) -> Optional[str]:
|
||||
|
||||
@@ -117,6 +117,7 @@ if TYPE_CHECKING:
|
||||
|
||||
from .fast_data_types import MousePosition
|
||||
from .file_transmission import FileTransmission
|
||||
from .notifications import OnlyWhen
|
||||
|
||||
|
||||
class CwdRequestType(Enum):
|
||||
@@ -630,7 +631,7 @@ class Window:
|
||||
self.current_mouse_event_button = 0
|
||||
self.current_clipboard_read_ask: Optional[bool] = None
|
||||
self.last_cmd_output_start_time = 0.
|
||||
self.last_notification_id: Optional[int] = None
|
||||
self.last_cmd_end_notification: Optional[tuple[int, 'OnlyWhen']] = None
|
||||
self.open_url_handler: 'OpenUrlHandler' = None
|
||||
self.last_cmd_cmdline = ''
|
||||
self.last_cmd_exit_status = 0
|
||||
@@ -1214,13 +1215,12 @@ class Window:
|
||||
tab = self.tabref()
|
||||
if tab is not None:
|
||||
tab.relayout_borders()
|
||||
if self.last_notification_id:
|
||||
# Notification id is saved withing handle_cmd_end so it
|
||||
# configured to be close upon focus is gained and visibility
|
||||
# change. When window is focused, it is visible for sure.
|
||||
nm = get_boss().notification_manager
|
||||
nm.close_notification(self.last_notification_id)
|
||||
self.last_notification_id = None
|
||||
if self.last_cmd_end_notification is not None:
|
||||
from .notifications import OnlyWhen
|
||||
opts = get_options()
|
||||
if self.last_cmd_end_notification[1] in (OnlyWhen.unfocused, OnlyWhen.invisible) and 'focus' in opts.notify_on_cmd_finish.clear_on:
|
||||
get_boss().notification_manager.close_notification(self.last_cmd_end_notification[0])
|
||||
self.last_cmd_end_notification = None
|
||||
elif self.os_window_id == current_focused_os_window_id():
|
||||
# Cancel IME composition after loses focus
|
||||
update_ime_position_for_window(self.id, False, -1)
|
||||
@@ -1518,7 +1518,7 @@ class Window:
|
||||
"is_start": False, "time": end_time, 'cmdline': self.last_cmd_cmdline, 'exit_status': self.last_cmd_exit_status})
|
||||
|
||||
opts = get_options()
|
||||
when, duration, action, notify_cmdline = opts.notify_on_cmd_finish
|
||||
when, duration, action, notify_cmdline, _ = opts.notify_on_cmd_finish
|
||||
|
||||
if last_cmd_output_duration >= duration and when != 'never':
|
||||
from .notifications import OnlyWhen
|
||||
@@ -1531,20 +1531,13 @@ class Window:
|
||||
if not nm.is_notification_allowed(cmd, self.id):
|
||||
return
|
||||
if action == 'notify':
|
||||
# Notification id is saved, so configuration was checked on
|
||||
# previous pass and saved to be cleared upon focus. But that
|
||||
# action was missed somehow and we should clear it here.
|
||||
if self.last_notification_id:
|
||||
nm.close_notification(self.last_notification_id)
|
||||
self.last_notification_id = None
|
||||
if self.last_cmd_end_notification is not None:
|
||||
if 'next' in opts.notify_on_cmd_finish.clear_on:
|
||||
nm.close_notification(self.last_cmd_end_notification[0])
|
||||
self.last_cmd_end_notification = None
|
||||
notification_id = nm.notify_with_command(cmd, self.id)
|
||||
# Saving notification id only when we are going to close it in
|
||||
# future.
|
||||
# TODO(Shvedov): We should close notification not only when
|
||||
# gather focus, but when window become visible if `when` equals
|
||||
# to "invisible".
|
||||
if cmd.only_when is OnlyWhen.unfocused or cmd.only_when is OnlyWhen.invisible:
|
||||
self.last_notification_id = notification_id
|
||||
if notification_id is not None:
|
||||
self.last_cmd_end_notification = notification_id, cmd.only_when
|
||||
elif action == 'bell':
|
||||
self.screen.bell()
|
||||
elif action == 'command':
|
||||
|
||||
Reference in New Issue
Block a user