Skip to content

Revamp the tui - #315

Merged
mikegros merged 44 commits into
cli-auth-inference-providersfrom
textual
Aug 27, 2026
Merged

Revamp the tui#315
mikegros merged 44 commits into
cli-auth-inference-providersfrom
textual

Conversation

@awadell1

Copy link
Copy Markdown
Collaborator

tl;dr Replace the Rich HITL with a Textural CLI

It's really nice...

  • feat(cli): add Textual terminal app
  • refactor(cli): replace Rich REPL with Textual app
  • style(cli): normalize config imports
  • refactor(cli): split Textual app into focused modules
  • test(cli): organize and strengthen Textual coverage
  • feat(cli): surface persistent agent sessions
  • docs(cli): describe Textual agent routing
  • fix(cli): grow prompt for wrapped text
  • feat(cli): harden Textual interactions and runtime

@awadell1
awadell1 force-pushed the textual branch 4 times, most recently from 9dbf68b to 548f5ab Compare August 11, 2026 23:30
@mikegros

Copy link
Copy Markdown
Collaborator

This is awesome. I only have one piece of feedback so far and that's that it is very frustrating that it no longer allows Ctrl-C copying from the terminal window.

@luiarthur

luiarthur commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

On a Mac, you can to alt (opt) + mouse to highlight a region. And then cmd+c to copy to clipboard. But this will include the text area boundaries.

Very nice that the pasting works as expected.

Separately when you hit "Shift Enter" the prompt is sent, instead of creating new lines, which was the case before. But is there a way to address that behavior with textual?

The copy issue is not a deal breaker for me because everything else is enhanced.

@awadell1

Copy link
Copy Markdown
Collaborator Author

@mikegros The clipboard should work now!

We use a platform-specific clipboard if possible, falling back to OSC52 (the default) if that doesn't work. If Ursa's running in an SSH session the default is OSC52, so the text ends up on the client. There's also a URSA_CLIPBOARD escape hatch if things get weird

@luiarthur Shift+Enter inserts a newline, Enter submits the prompts. At least that's what it's doing on my system and should be doing. Am I misunderstanding the comment? One oddity is the prompt area is set to be markdown, so a message like this:

hello
bob

Shows up as hello bob in the chat history. The upside is that code blocks render correctly.
I've tried using Textual's tree-sitter syntax highlighting for PromptArea styling, but out of the box it's pretty limited so I'm leaving that a future work.

@awadell1 awadell1 changed the title Revamp the cli Revamp the tui Aug 20, 2026
@luiarthur

luiarthur commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

@mikegros The clipboard should work now!

We use a platform-specific clipboard if possible, falling back to OSC52 (the default) if that doesn't work. If Ursa's running in an SSH session the default is OSC52, so the text ends up on the client. There's also a URSA_CLIPBOARD escape hatch if things get weird

@luiarthur Shift+Enter inserts a newline, Enter submits the prompts. At least that's what it's doing on my system and should be doing. Am I misunderstanding the comment? One oddity is the prompt area is set to be markdown, so a message like this:

hello
bob

Shows up as hello bob in the chat history. The upside is that code blocks render correctly. I've tried using Textual's tree-sitter syntax highlighting for PromptArea styling, but out of the box it's pretty limited so I'm leaving that a future work.

Thanks! I can copy to clip board on Mac in both default terminal and iterm2 using Ctrl+c. (For others, cmd+c on Mac won't work.) Works locally, but not over ssh. I know ssh is a much harder issue.

But shift+enter still submits the prompt regardless of terminal, locally and over ssh.

@awadell1

Copy link
Copy Markdown
Collaborator Author

Shift+Enter not working appears to be terminal specific. In Ghostty the new TUI works and the old doesn't. In Terminal.app (and possibly iTerm2) the reverse is true. With the caveat that in Terminal.app a multi-line prompt is submitted multiple times, so also was kinda borked.

On the plus side the new TUI in the Terminal.app does support multi-line prompts being pasted in.

Thanks! I can copy to clip board on Mac in both default terminal and iterm2 using Ctrl+c. (For others, cmd+c on Mac won't work.) Works locally, but not over ssh. I know ssh is a much harder issue.

Copy over ssh will only work if the terminal supports OSC52. That needs to be enabled in iTerm2 to work, but is not supported by Terminal.app (Mac). I'm not aware of an alternative to OSC52 for copy-over-ssh. But if there is, and the URSA_CLIPBOARD isn't a sufficient solution that would be good to know.

Talking to @mikegros, support for the out-of-the-box experience (ie. Terminal.app) is important. I'm going to keep poking and see if I can get that OOTB behavior better on Terminal.app. At a minimum the keymaps reported to work, should work.

@awadell1

Copy link
Copy Markdown
Collaborator Author

Cross-terminal support should be fixed, the core issue is that getting nice keymaps in a tui only works if the terminal supports it.
For textual that means supporting the kitty keyboard protocol, which a lot of the "modern" terminals do (ghostty, wezterm, the new windows terminal, iterm2 (if enabled)).

The fix I just pushed is this:

  1. try to detect if the kitty keyboard-protocol is supported. This isn't exact as doing it exact messes with textual.
  2. If supported, indicate Shift+Enter as the newline keymap, otherwise advertise Ctrl+J

Either way the keymaps don't change. /keymap shows both and both are active, detection just controls what get's advertised.
I've also added a warning to /keymap indicating if kitty support was / was not detected.

As an aside, Shift+Enter on a traditional terminal is sent as the same keycode as Enter. The old TUI got around this by using readline, but only sort of.
As a point of reference, codex in Terminal.app (at least mine) also treats Shift+enter as Enter.

@BrennanTM

Copy link
Copy Markdown
Contributor

Looked into the Windows CI failure (test_user_scroll_cancels_initial_anchor_transition) since it was the one item in the thread nobody had picked up yet. It is a real race rather than a flaky assertion. scroll_end(animate=True) defers the scroll until the next refresh unless immediate=True is passed, so _anchor_conversation_if_overflowing sets _conversation_anchor_transition before its animation exists. A user scroll in that gap cannot stop an animation that has not started, and when the deferred animation begins it drives the viewport from the user's position to the bottom. That is the 0.946 then 3 in the log (with is_anchored still false because on_complete had not fired), and on a slow terminal it is a user getting pulled back down right after scrolling up. It reproduces deterministically on macOS by scrolling inside the gap; the Windows runner just makes the gap wide.

The fix is one line in _anchor_conversation_if_overflowing:

             conversation.scroll_end(
                 animate=True,
                 duration=0.15,
+                immediate=True,
                 on_complete=lambda: self._finish_conversation_anchor(
                     generation
                 ),
             )

With the animation running from the moment the flag is set, any later user scroll goes through _scroll_to, which force-stops it, and force_stop_animation runs no callbacks, so the interrupted transition stays cancelled until the next prompt resets it. Two commits are on BrennanTM/ursa@tui-anchor-immediate-start if a cherry-pick is convenient: a red-first regression test (it fails at "the animation is already running when the flag is set" without the fix, and pins the viewport staying put afterward) and the one-liner. tests/cli passes at 164 on that branch. Happy to open it as a PR against textual instead if that is easier.

The remaining Windows failures in the branch's run history (plan spinner, plan card, Ctrl-C) look like fixed asyncio.sleep waits on timers in the tests rather than product behavior; glad to take those too if useful.

@awadell1

Copy link
Copy Markdown
Collaborator Author

Thanks @BrennanTM I've cherry-picked the commits onto this branch

@awadell1
awadell1 changed the base branch from main to cli-auth-inference-providers August 24, 2026 21:38
@awadell1
awadell1 force-pushed the textual branch 2 times, most recently from 04e8a9e to 14ddb07 Compare August 25, 2026 00:03
```
### Full-screen interface controls

Type `help` or `?` inside the prompt to see available interactive commands.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure where to put this comment, but every now and then, ursa will say something like,

To see available URSA commands, type: help or ?

which has been replaced by things like / commands. I know we baked this in as a system prompt somewhere... wherever this shows up, could you remove it?

@luiarthur

luiarthur commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

@awadell1 In the tui, when you type /model, from where are the inference providers populated? I see the following, which includes only OpenAI's URL. Can I either type my own URL or have the URL populate from somewhere? Same question for embedding models.

Screenshot 2026-08-25 at 3 08 02 PM

@mikegros

Copy link
Copy Markdown
Collaborator

@awadell1 In the tui, when you type /model, from where are the inference providers populated? I see the following, which includes only OpenAI's URL. Can I either type my own URL or have the URL populate from somewhere? Same question for embedding models.

Screenshot 2026-08-25 at 3 08 02 PM

The config updates in this PR allowed a 'inference_provider' section of the config files. If you use a config file with that, it will populate the inference provider info into that box. If you use the current config format (like the current example in configs/example.yaml), it does not.

BrennanTM and others added 12 commits August 25, 2026 18:54
scroll_end defers to the next refresh unless immediate is set, which
left a gap between the transition flag and the running animation. A
user scroll landing in that gap was overridden once the animation
began, which is the Windows CI failure in
test_user_scroll_cancels_initial_anchor_transition and a real
interaction bug on slow terminals. Starting the animation synchronously
lets any later user scroll force-stop it, and no callbacks fire on that
stop, so the interrupted transition stays cancelled until the next
prompt resets it.
@awadell1

Copy link
Copy Markdown
Collaborator Author

@luiarthur I've update the /model modal to include [?] that explain what they are and that adding inference providers is config only

/model now knows about the models offered by the endpoints

Ctrl+d now exits a bit less aggressively, which hopefully will bork fewer terminals

@awadell1
awadell1 force-pushed the textual branch 3 times, most recently from e9c57d0 to a7df1a5 Compare August 26, 2026 17:40
@awadell1
awadell1 force-pushed the textual branch 5 times, most recently from ba3c3bc to 0f19bc3 Compare August 26, 2026 18:47
@mikegros
mikegros merged commit df37d0e into main Aug 27, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants