mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
de30488b20
> This PR was authored by Mux on behalf of Mike. Adds `coder exp agents`, an interactive terminal UI for managing Coder AI agent chats. Built with bubbletea/lipgloss/glamour, the TUI provides parity with the web dashboard for chat management, model selection, and real-time tool execution visibility. ## What it does - **Chat list view**: tree-based navigation with nested subagent expansion, search filtering, windowed scrolling, and pagination. - **Active chat view**: viewport-based transcript with markdown rendering, WebSocket streaming, and a text input composer for sending messages. - **Model picker overlay**: cached model catalog with fuzzy selection. - **Diff drawer overlay**: git changes inspection with unified diff rendering. - **Tool call rendering**: humanized argument summaries, consecutive duplicate collapsing, and status indicators. ## Key implementation details - Session lifecycle uses a monotonic `chatGeneration` counter so async responses from stale sessions are dropped on chat switch. - Draft mode guards prevent duplicate chat creation on double-Enter. - Error and loading states render inline without collapsing the TUI chrome. - Glamour renderer access is mutex-protected (not thread-safe). - Intentional WebSocket close is distinguished from dropped connections to prevent spurious reconnects. ## Testing ~220 unit tests covering rendering, state transitions, keyboard dispatch, and edge cases. 4-scenario PTY-based E2E suite covers boot, navigation, search, and direct chat open. 14 new files, ~7,400 lines added.
34 lines
625 B
Go
34 lines
625 B
Go
package cli
|
|
|
|
import (
|
|
"regexp"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
var terminalEscapeSequenceRegexp = regexp.MustCompile(
|
|
`\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]|` +
|
|
"" + `[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]|` +
|
|
`\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)|` +
|
|
"" + `[^\x07\x1b]*(?:\x07|\x1b\\)|` +
|
|
`\x1b[^\[\]].`,
|
|
)
|
|
|
|
func sanitizeTerminalRenderableText(text string) string {
|
|
if text == "" {
|
|
return ""
|
|
}
|
|
|
|
text = terminalEscapeSequenceRegexp.ReplaceAllString(text, "")
|
|
return strings.Map(func(r rune) rune {
|
|
switch r {
|
|
case '\n', '\t':
|
|
return r
|
|
}
|
|
if unicode.IsControl(r) {
|
|
return -1
|
|
}
|
|
return r
|
|
}, text)
|
|
}
|