Skip to content

Bunli should provide a higher-level render lifecycle on top of OpenTUI #66

Description

@imjlk

Bunli currently exposes useRuntime().exit() as the main way to finish a render-based command.

That makes sense as a thin wrapper over OpenTUI's manual cleanup model, but at the Bunli level it feels too low-level and easy to miss.

In practice, when building a render command, most authors are thinking in terms of:

  • complete the flow
  • cancel the flow
  • dismiss the screen

not "shut down the renderer."

Because of that, it is easy to forget to call runtime.exit() in one of the submit / cancel / quit paths, which leaves the TUI hanging indefinitely.

Why this feels like a Bunli issue

OpenTUI itself is a lower-level renderer and explicitly expects applications to manage cleanup with renderer.destroy().

Bunli, however, is a higher-level CLI framework. Since it already wraps OpenTUI with useRuntime().exit(), it seems like a good place to provide a more semantic completion model for render commands instead of making every app manage renderer shutdown directly.

Suggested direction

It would be easier to use correctly if Bunli exposed higher-level lifecycle APIs such as:

  • useRuntime().complete()
  • useRuntime().cancel()

with exit() remaining available as the low-level primitive.

That would make common flows more natural:

  • onSubmit -> complete()
  • onCancel -> cancel()
  • onQuit -> cancel() or complete()

Possible follow-ups

Higher-level components could also optionally integrate with this automatically, for example:

  • form-style components could support closeOnSubmit / closeOnCancel
  • pager-style components could support a built-in close behavior on quit
  • development mode could warn when a render-only command never completes

Summary

This is less about changing OpenTUI's lifecycle model and more about Bunli providing a better authoring model on top of it.

Right now, render commands are easy to leave hanging because the only completion primitive exposed at the Bunli level is a manual renderer exit call.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions