From d6886e0ccc8b4ca3ebf1282430aec314f6d4181d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 17 May 2026 15:57:50 +0000 Subject: [PATCH] Add dnd_command support to --dump-commands in client.py Agent-Logs-Url: https://github.com/kovidgoyal/kitty/sessions/aa0821cf-6cf1-4fe5-ad76-904dba97aefa Co-authored-by: kovidgoyal <1308621+kovidgoyal@users.noreply.github.com> --- kitty/boss.py | 7 ++++++- kitty/client.py | 28 ++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/kitty/boss.py b/kitty/boss.py index 57c415e99..748129f7e 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -304,11 +304,16 @@ class DumpCommands: # {{{ if self.draw_dump_buf: safe_print('draw', ''.join(self.draw_dump_buf)) self.draw_dump_buf = [] + def encode_dict_value(v: Any) -> Any: + if isinstance(v, (bytes, memoryview)): + return str(v, 'utf-8', 'replace') + return v + def fmt(x: Any) -> Any: if isinstance(x, (bytes, memoryview)): return str(x, 'utf-8', 'replace') if isinstance(x, dict): - return json.dumps(x) + return json.dumps({k: encode_dict_value(v) for k, v in x.items()}) return x safe_print(what, *map(fmt, a), flush=True) # }}} diff --git a/kitty/client.py b/kitty/client.py index 5f15ce95f..871fe0b71 100644 --- a/kitty/client.py +++ b/kitty/client.py @@ -12,7 +12,7 @@ import sys from contextlib import suppress from typing import Any -from .fast_data_types import TEXT_SIZE_CODE +from .fast_data_types import DND_CODE, TEXT_SIZE_CODE CSI = '\x1b[' OSC = '\x1b]' @@ -278,6 +278,30 @@ def clipboard_control(payload: str) -> None: write(f'{OSC}{payload}\x07') +def dnd_command(payload: str) -> None: + c = json.loads(payload) + text = c.pop('', '') + t = c.pop('type', 'a') + if isinstance(t, (bytes, memoryview)): + t = str(t, 'utf-8', 'replace') + m = f't={t}' + if (more := c.pop('more', None)): + m += f':m={more}' + if (client_id := c.pop('client_id', None)): + m += f':i={client_id}' + if (operation := c.pop('operation', None)) is not None and operation: + m += f':o={operation}' + if (cell_x := c.pop('cell_x', None)): + m += f':x={cell_x}' + if (cell_y := c.pop('cell_y', None)): + m += f':y={cell_y}' + if (pixel_x := c.pop('pixel_x', None)): + m += f':X={pixel_x}' + if (pixel_y := c.pop('pixel_y', None)): + m += f':Y={pixel_y}' + write(f'{OSC}{DND_CODE};{m};{text}\x1b\\') + + def multicell_command(payload: str) -> None: c = json.loads(payload) text = c.pop('', '') @@ -306,7 +330,7 @@ def screen_multi_cursor(rest: str) -> None: def replay(raw: str) -> None: specials = frozenset({ 'draw', 'set_title', 'set_icon', 'set_dynamic_color', 'set_color_table_color', 'select_graphic_rendition', - 'process_cwd_notification', 'clipboard_control', 'shell_prompt_marking', 'multicell_command', 'screen_multi_cursor', + 'process_cwd_notification', 'clipboard_control', 'shell_prompt_marking', 'multicell_command', 'screen_multi_cursor', 'dnd_command', }) for line in raw.splitlines(): if line.strip() and not line.startswith('#'):