prompt-toolkit / python-prompt-toolkit

Library for building powerful interactive command line applications in Python
https://python-prompt-toolkit.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
9.11k stars 717 forks source link

prevent HSplit/VSplit from growing children to fill all available space #1757

Open LoganDark opened 1 year ago

LoganDark commented 1 year ago

I'm having issues trying to create a really simple chat program for an LLM (large language model), RWKV. I've used just basic input() for my last script, but things that have been written to the terminal can't be modified then, so I can't provide a good user experience for undo and regen and editing and etc. I thought a TUI library might help with this, and I remembered that prompt_toolkit seems to support creating really rich experiences (I remember ptpython very fondly), but I'm already just about ready to give up ...

for example if I just want to display a list of messages to the terminal, I can use a non-fullscreen application and an HSplit containing the list of messages, and at first it appears to work properly:

  1. The messages only take up the space that is required

    image

  2. The space grows to fill if you put newlines in a message

    image

but there is an issue:

there are branches in the source code dedicated to doing this (forcing the layout to never shrink its height), until the app exits. only then is it shrunk, as it should have been while it was running (because then app.is_done is true, and the layout uses THAT as its signal to shrink).

do I need to create a custom subclass in order to represent multiple messages in a simple chat log ? there seems to be no way to prevent prompt_toolkit from growing the items like this, but I don't want that, I want the layout to shrink again if the minimum size decreases. :/

kind of frustrated because it seems like this library is perfectly capable of doing what I want, and even does it in certain situations, but doesn't let me configure it and seems to be explicitly designed to do the opposite by force

this seems like a really really basic thing - is there no basic stack layout? only linear row/column layouts? :/

(I could not find any suitable issue about this specifically, searching for things like grow, flex, hsplit, vsplit, layout, stack, list, chat etc.)

joouha commented 12 months ago

is there no basic stack layout?

Try setting dont_extend_height=True on your Windows inside your HSplit like this:

from prompt_toolkit.application import Application
from prompt_toolkit.key_binding import KeyBindings
from prompt_toolkit.layout.containers import HSplit, Window
from prompt_toolkit.layout.controls import FormattedTextControl
from prompt_toolkit.layout.layout import Layout

kb = KeyBindings()
kb.add("c-c")(lambda e: e.app.exit())

application = Application(
    layout=Layout(
        HSplit(
            [
                Window(
                    content=FormattedTextControl("Me:  AAAA"), dont_extend_height=True
                ),
                Window(
                    content=FormattedTextControl("You: BBBBB\n     bbbbb"),
                    dont_extend_height=True,
                ),
                Window(
                    content=FormattedTextControl("Me:  CCCC"), dont_extend_height=True
                ),
            ],
            padding_char="-",
            padding=1,
        )
    ),
    key_bindings=kb,
)

if __name__ == "__main__":
    application.run()

image

do I need to create a custom subclass in order to represent multiple messages in a simple chat log ?

You can implement a VStack pretty easily - I've implemented something similar here which you might find a useful reference: https://github.com/joouha/euporie/blob/f7710f7d65eb9f6d726fc9c18dc8ab5cc6d49f11/euporie/core/widgets/page.py#L984C1-L1084C13

If you are trying to build a REPL style chat application, you're probably better off using a PromptSession and printing the output to the terminal.

LoganDark commented 12 months ago

If you are trying to build a REPL style chat application, you're probably better off using a PromptSession and printing the output to the terminal.

This is append-only where I want to be able to change history in-place.

I've settled for a fullscreen application that just uses get_line_prefix on a buffer control. See here:

https://github.com/prompt-toolkit/python-prompt-toolkit/assets/4723091/0644d72e-ed0e-496e-90dc-31540a69acc7

I'll still consider this an issue until either some stack container is added to prompt_toolkit officially, or HSplit gains an option to disable this undesired force-growing behavior.