resize_window kitten: Go TUI implementation and registration

This commit is contained in:
OBrutus 2026-08-11 00:27:05 +05:30
parent 9e34cad010
commit edbf66f004
4 changed files with 267 additions and 1 deletions

51
kittens/resize_window/cli_generated.go generated Normal file
View file

@ -0,0 +1,51 @@
// Code generated by go_code.py; DO NOT EDIT.
package resize_window
import "fmt"
import "github.com/kovidgoyal/kitty/tools/cli"
var _ = fmt.Sprintf
func create_cmd(root *cli.Command, run_func func(*cli.Command, *Options, []string)(int, error)) {
ans := root.AddSubCommand(&cli.Command{
Name: "resize_window",
ShortDescription: "Resize the current window",
HelpText: "Resize the current window",
Run: func(cmd *cli.Command, args []string) (int, error) {
opts := Options{}
err := cmd.GetOptionValues(&opts)
if err != nil { return 1, err }
return run_func(cmd, &opts, args)},
Hidden: true,
})
ans.Add(cli.OptionSpec{
Name: "--horizontal-increment",
Type: "int",
Dest: "HorizontalIncrement",
Help: "The base horizontal increment.",
Default: "2",
})
ans.Add(cli.OptionSpec{
Name: "--vertical-increment",
Type: "int",
Dest: "VerticalIncrement",
Help: "The base vertical increment.",
Default: "2",
})
clone := root.AddClone(ans.Group, ans)
clone.Hidden = false
clone.Name = "resize-window"
}
type Options struct {
HorizontalIncrement int
VerticalIncrement int
}
func (opts Options) AsCommandLine() (ans []string) {
sval := ""
_ = sval
sval = fmt.Sprintf(`%d`, opts.HorizontalIncrement)
if (sval != `2`) { ans = append(ans, `--horizontal-increment=` + sval)}
sval = fmt.Sprintf(`%d`, opts.VerticalIncrement)
if (sval != `2`) { ans = append(ans, `--vertical-increment=` + sval)}
return
}

View file

@ -0,0 +1,200 @@
// License: GPLv3 Copyright: 2026, Kovid Goyal, <kovid at kovidgoyal.net>
package resize_window
import (
"encoding/json"
"fmt"
"os"
"strings"
"github.com/kovidgoyal/kitty/tools/cli"
"github.com/kovidgoyal/kitty/tools/cli/markup"
"github.com/kovidgoyal/kitty/tools/tui"
"github.com/kovidgoyal/kitty/tools/tui/loop"
)
var _ = fmt.Print
type handler struct {
lp *loop.Loop
opts *Options
ctx *markup.Context
original_size loop.ScreenSize
print_on_fail string
}
type rc_response struct {
Ok bool `json:"ok"`
Error string `json:"error"`
Tb string `json:"tb"`
Data json.RawMessage `json:"data"`
}
func (h *handler) do_window_resize(is_decrease, is_horizontal, reset bool, multiplier int) error {
increment := h.opts.HorizontalIncrement
if !is_horizontal {
increment = h.opts.VerticalIncrement
}
increment *= multiplier
if is_decrease {
increment = -increment
}
axis := "reset"
if !reset {
if is_horizontal {
axis = "horizontal"
} else {
axis = "vertical"
}
}
ec, err := resize_command_escape_code(increment, axis)
if err != nil {
return err
}
h.lp.QueueWriteString(ec)
return nil
}
// json_is_truthy mirrors Python's truthiness test (`if value:`) for a decoded
// JSON value, so on_rc_response's "beep if data is truthy" check matches the
// behavior of kittens/resize_window/main.py's on_kitty_cmd_response. The
// resize-window RC command returns bool | None | str for "data" (see
// kitty/rc/resize_window.py), so False/None/"" must all be treated as falsy.
func json_is_truthy(raw json.RawMessage) bool {
if len(raw) == 0 {
return false
}
var v any
if err := json.Unmarshal(raw, &v); err != nil {
return false
}
switch val := v.(type) {
case nil:
return false
case bool:
return val
case float64:
return val != 0
case string:
return val != ""
case []any:
return len(val) != 0
case map[string]any:
return len(val) != 0
default:
return true
}
}
func (h *handler) on_rc_response(raw []byte) error {
var response rc_response
if err := json.Unmarshal(raw, &response); err != nil {
return err
}
if !response.Ok {
emsg := response.Error
if response.Tb != "" {
emsg += "\n" + response.Tb
}
h.print_on_fail = emsg
h.lp.Quit(1)
return nil
}
if json_is_truthy(response.Data) {
h.lp.Beep()
}
return nil
}
func (h *handler) on_text(text string) error {
text = strings.ToUpper(text)
switch text {
case "W", "N", "T", "S", "R":
return h.do_window_resize(text == "N" || text == "S", text == "W" || text == "N", text == "R", 1)
case "Q":
h.lp.Quit(0)
}
return nil
}
func (h *handler) on_key(e *loop.KeyEvent) error {
if e.MatchesPressOrRepeat("esc") {
e.Handled = true
h.lp.Quit(0)
return nil
}
for _, k := range []string{"w", "n", "t", "s"} {
if e.MatchesPressOrRepeat("ctrl+" + k) {
e.Handled = true
return h.do_window_resize(k == "n" || k == "s", k == "w" || k == "n", false, 2)
}
}
return nil
}
func (h *handler) draw_screen() {
lp, ctx := h.lp, h.ctx
lp.StartAtomicUpdate()
defer lp.EndAtomicUpdate()
lp.ClearScreen()
lp.Println(lp.SprintStyled("bold fg=white", "Resize this window"))
lp.Println()
lp.Println("Press one of the following keys:")
lp.Println(" " + ctx.Green("W") + "ider")
lp.Println(" " + ctx.Green("N") + "arrower")
lp.Println(" " + ctx.Green("T") + "aller")
lp.Println(" " + ctx.Green("S") + "horter")
lp.Println(" " + ctx.Red("R") + "eset")
lp.Println()
lp.Println("Press " + lp.SprintStyled("italic", "Esc") + " to quit resize mode")
lp.Println("Hold down " + lp.SprintStyled("italic", "Ctrl") + " to double step size")
lp.Println()
lp.Println(lp.SprintStyled("bold fg=white", "Sizes"))
lp.Printf("Original: %d rows %d cols\r\n", h.original_size.HeightCells, h.original_size.WidthCells)
if sz, err := lp.ScreenSize(); err == nil {
lp.Printf("Current: %s rows %s cols\r\n",
ctx.Magenta(fmt.Sprint(sz.HeightCells)), ctx.Magenta(fmt.Sprint(sz.WidthCells)))
}
}
func run_loop(opts *Options) (rc int, err error) {
lp, err := loop.New(loop.FullKeyboardProtocol)
if err != nil {
return 1, err
}
h := &handler{lp: lp, opts: opts, ctx: markup.New(true)}
lp.OnInitialize = func() (string, error) {
sz, err := lp.ScreenSize()
if err != nil {
return "", err
}
h.original_size = sz
lp.SetCursorVisible(false)
lp.AllowLineWrapping(false)
h.draw_screen()
return "", nil
}
lp.OnResize = func(old, new loop.ScreenSize) error { h.draw_screen(); return nil }
lp.OnText = func(text string, from_key_event, in_bracketed_paste bool) error { return h.on_text(text) }
lp.OnKeyEvent = h.on_key
lp.OnRCResponse = h.on_rc_response
err = lp.Run()
if err != nil {
return 1, err
}
lp.KillIfSignalled()
if h.print_on_fail != "" {
fmt.Fprintln(os.Stderr, h.print_on_fail)
tui.HoldTillEnter(false)
}
return lp.ExitCode(), nil
}
func main(cmd *cli.Command, opts *Options, args []string) (rc int, err error) {
return run_loop(opts)
}
func EntryPoint(parent *cli.Command) {
create_cmd(parent, main)
}

View file

@ -112,10 +112,12 @@ default=2
type=int
The base vertical increment.
'''.format
help_text = 'Resize the current window'
usage = ''
def main(args: list[str]) -> None:
msg = 'Resize the current window'
msg = help_text
try:
cli_opts, items = parse_args(args[1:], OPTIONS, '', msg, 'resize_window', result_class=ResizeCLIOptions)
except SystemExit as e:
@ -131,3 +133,13 @@ def main(args: list[str]) -> None:
print(handler.print_on_fail, file=sys.stderr)
input('Press Enter to quit')
raise SystemExit(loop.return_code)
if __name__ == '__main__':
main(sys.argv)
elif __name__ == '__doc__':
cd = sys.cli_docs # type: ignore
cd['usage'] = usage
cd['options'] = OPTIONS
cd['help_text'] = help_text
cd['short_desc'] = help_text

View file

@ -20,6 +20,7 @@ import (
"github.com/kovidgoyal/kitty/kittens/panel"
"github.com/kovidgoyal/kitty/kittens/query_terminal"
"github.com/kovidgoyal/kitty/kittens/quick_access_terminal"
"github.com/kovidgoyal/kitty/kittens/resize_window"
"github.com/kovidgoyal/kitty/kittens/show_key"
"github.com/kovidgoyal/kitty/kittens/ssh"
"github.com/kovidgoyal/kitty/kittens/themes"
@ -66,6 +67,8 @@ func KittyToolEntryPoints(root *cli.Command) {
panel.EntryPoint(root)
// quick_access_terminal
quick_access_terminal.EntryPoint(root)
// resize_window
resize_window.EntryPoint(root)
// unicode_input
unicode_input.EntryPoint(root)
// show_key