This commit is contained in:
Ed Zynda
2025-06-27 16:31:01 +03:00
parent 00aa3c6995
commit 7b2592bbdc
5 changed files with 59 additions and 57 deletions
+1 -1
View File
@@ -384,7 +384,7 @@ func runNormalMode(ctx context.Context) error {
if err != nil {
return fmt.Errorf("failed to create CLI: %v", err)
}
// Set the model name for consistent display
cli.SetModelName(modelName)
+3 -3
View File
@@ -21,13 +21,13 @@ var (
// CLI handles the command line interface with improved message rendering
type CLI struct {
messageRenderer *MessageRenderer
compactRenderer *CompactRenderer // Add compact renderer
compactRenderer *CompactRenderer // Add compact renderer
messageContainer *MessageContainer
usageTracker *UsageTracker
width int
height int
compactMode bool // Add compact mode flag
modelName string // Store current model name
compactMode bool // Add compact mode flag
modelName string // Store current model name
}
// NewCLI creates a new CLI instance with message container
+41 -39
View File
@@ -32,14 +32,14 @@ func (r *CompactRenderer) RenderUserMessage(content string, timestamp time.Time)
theme := getTheme()
symbol := lipgloss.NewStyle().Foreground(theme.Secondary).Render(">")
label := lipgloss.NewStyle().Foreground(theme.Secondary).Bold(true).Render("User")
// Format content for user messages (preserve formatting, no truncation)
compactContent := r.formatUserAssistantContent(content)
// Handle multi-line content
lines := strings.Split(compactContent, "\n")
var formattedLines []string
for i, line := range lines {
if i == 0 {
// First line includes symbol and label
@@ -49,7 +49,7 @@ func (r *CompactRenderer) RenderUserMessage(content string, timestamp time.Time)
formattedLines = append(formattedLines, line)
}
}
return UIMessage{
Type: UserMessage,
Content: strings.Join(formattedLines, "\n"),
@@ -62,23 +62,23 @@ func (r *CompactRenderer) RenderUserMessage(content string, timestamp time.Time)
func (r *CompactRenderer) RenderAssistantMessage(content string, timestamp time.Time, modelName string) UIMessage {
theme := getTheme()
symbol := lipgloss.NewStyle().Foreground(theme.Primary).Render("<")
// Use the full model name, fallback to "Assistant" if empty
if modelName == "" {
modelName = "Assistant"
}
label := lipgloss.NewStyle().Foreground(theme.Primary).Bold(true).Render(modelName)
// Format content for assistant messages (preserve formatting, no truncation)
compactContent := r.formatUserAssistantContent(content)
if compactContent == "" {
compactContent = lipgloss.NewStyle().Foreground(theme.Muted).Italic(true).Render("(no output)")
}
// Handle multi-line content
lines := strings.Split(compactContent, "\n")
var formattedLines []string
for i, line := range lines {
if i == 0 {
// First line includes symbol and label
@@ -88,7 +88,7 @@ func (r *CompactRenderer) RenderAssistantMessage(content string, timestamp time.
formattedLines = append(formattedLines, line)
}
}
return UIMessage{
Type: AssistantMessage,
Content: strings.Join(formattedLines, "\n"),
@@ -102,15 +102,15 @@ func (r *CompactRenderer) RenderToolCallMessage(toolName, toolArgs string, times
theme := getTheme()
symbol := lipgloss.NewStyle().Foreground(theme.Tool).Render("[")
label := lipgloss.NewStyle().Foreground(theme.Tool).Bold(true).Render(toolName)
// Format args for compact display
argsDisplay := r.formatToolArgs(toolArgs)
if argsDisplay != "" {
argsDisplay = lipgloss.NewStyle().Foreground(theme.Muted).Render(argsDisplay)
}
line := fmt.Sprintf("%s %s %s", symbol, label, argsDisplay)
return UIMessage{
Type: ToolCallMessage,
Content: line,
@@ -123,12 +123,12 @@ func (r *CompactRenderer) RenderToolCallMessage(toolName, toolArgs string, times
func (r *CompactRenderer) RenderToolMessage(toolName, toolArgs, toolResult string, isError bool) UIMessage {
theme := getTheme()
symbol := lipgloss.NewStyle().Foreground(theme.Muted).Render("]")
// Determine result type and styling
var label string
var content string
var labelText string
if isError {
labelText = "Error"
label = lipgloss.NewStyle().Foreground(theme.Muted).Bold(true).Render(labelText)
@@ -138,16 +138,16 @@ func (r *CompactRenderer) RenderToolMessage(toolName, toolArgs, toolResult strin
labelText = r.determineResultType(toolName, toolResult)
label = lipgloss.NewStyle().Foreground(theme.Muted).Bold(true).Render(labelText)
content = lipgloss.NewStyle().Foreground(theme.Muted).Render(r.formatToolResult(toolResult))
if r.formatToolResult(toolResult) == "" {
content = lipgloss.NewStyle().Foreground(theme.Muted).Italic(true).Render("(no output)")
}
}
// Handle multi-line tool results
contentLines := strings.Split(content, "\n")
var formattedLines []string
for i, line := range contentLines {
if i == 0 {
// First line includes symbol and label
@@ -157,7 +157,7 @@ func (r *CompactRenderer) RenderToolMessage(toolName, toolArgs, toolResult strin
formattedLines = append(formattedLines, line)
}
}
return UIMessage{
Type: ToolMessage,
Content: strings.Join(formattedLines, "\n"),
@@ -170,11 +170,11 @@ func (r *CompactRenderer) RenderSystemMessage(content string, timestamp time.Tim
theme := getTheme()
symbol := lipgloss.NewStyle().Foreground(theme.System).Render("*")
label := lipgloss.NewStyle().Foreground(theme.System).Bold(true).Render("System")
compactContent := r.formatCompactContent(content)
line := fmt.Sprintf("%s %-8s %s", symbol, label, compactContent)
return UIMessage{
Type: SystemMessage,
Content: line,
@@ -188,11 +188,11 @@ func (r *CompactRenderer) RenderErrorMessage(errorMsg string, timestamp time.Tim
theme := getTheme()
symbol := lipgloss.NewStyle().Foreground(theme.Error).Render("!")
label := lipgloss.NewStyle().Foreground(theme.Error).Bold(true).Render("Error")
compactContent := lipgloss.NewStyle().Foreground(theme.Error).Render(r.formatCompactContent(errorMsg))
line := fmt.Sprintf("%s %-8s %s", symbol, label, compactContent)
return UIMessage{
Type: ErrorMessage,
Content: line,
@@ -206,7 +206,7 @@ func (r *CompactRenderer) RenderDebugConfigMessage(config map[string]any, timest
theme := getTheme()
symbol := lipgloss.NewStyle().Foreground(theme.Tool).Render("*")
label := lipgloss.NewStyle().Foreground(theme.Tool).Bold(true).Render("Debug")
// Format config as compact key=value pairs
var configPairs []string
for key, value := range config {
@@ -214,14 +214,14 @@ func (r *CompactRenderer) RenderDebugConfigMessage(config map[string]any, timest
configPairs = append(configPairs, fmt.Sprintf("%s=%v", key, value))
}
}
content := strings.Join(configPairs, ", ")
if len(content) > r.width-20 {
content = content[:r.width-23] + "..."
}
line := fmt.Sprintf("%s %-8s %s", symbol, label, content)
return UIMessage{
Type: SystemMessage,
Content: line,
@@ -235,18 +235,18 @@ func (r *CompactRenderer) formatCompactContent(content string) string {
if content == "" {
return ""
}
// Remove markdown formatting for compact display
content = strings.ReplaceAll(content, "\n", " ")
content = strings.ReplaceAll(content, "\t", " ")
// Collapse multiple spaces
for strings.Contains(content, " ") {
content = strings.ReplaceAll(content, " ", " ")
}
content = strings.TrimSpace(content)
// Truncate if too long (unless in debug mode)
maxLen := r.width - 28 // Reserve space for symbol and label more conservatively
if maxLen < 40 {
@@ -255,7 +255,7 @@ func (r *CompactRenderer) formatCompactContent(content string) string {
if !r.debug && len(content) > maxLen {
content = content[:maxLen-3] + "..."
}
return content
}
@@ -320,12 +320,13 @@ func (r *CompactRenderer) wrapText(text string, width int) string {
return strings.Join(wrappedLines, "\n")
}
// formatToolArgs formats tool arguments for compact display
func (r *CompactRenderer) formatToolArgs(args string) string {
if args == "" || args == "{}" {
return ""
}
// Remove JSON braces and format compactly
args = strings.TrimSpace(args)
if strings.HasPrefix(args, "{") && strings.HasSuffix(args, "}") {
@@ -333,16 +334,16 @@ func (r *CompactRenderer) formatToolArgs(args string) string {
args = strings.TrimSuffix(args, "}")
args = strings.TrimSpace(args)
}
// Remove quotes around simple values
args = strings.ReplaceAll(args, `"`, "")
// Remove parameter names (e.g., "command: ls" -> "ls", "path: /home" -> "/home")
// Look for pattern "key: value" and extract just the value
if colonIndex := strings.Index(args, ":"); colonIndex != -1 {
args = strings.TrimSpace(args[colonIndex+1:])
}
return r.formatCompactContent(args)
}
@@ -375,10 +376,11 @@ func (r *CompactRenderer) formatToolResult(result string) string {
return strings.Join(lines, "\n")
}
// determineResultType determines the display type for tool results
func (r *CompactRenderer) determineResultType(toolName, result string) string {
toolName = strings.ToLower(toolName)
switch {
case strings.Contains(toolName, "read"):
return "Text"
@@ -395,4 +397,4 @@ func (r *CompactRenderer) determineResultType(toolName, result string) string {
default:
return "Result"
}
}
}
+2 -2
View File
@@ -2,7 +2,7 @@ package ui
import (
"fmt"
"github.com/charmbracelet/lipgloss"
)
@@ -241,6 +241,6 @@ func FormatCompactLine(symbol, label, content string, symbolColor, labelColor, c
styledSymbol := StyleCompactSymbol(symbol, symbolColor).Render(symbol)
styledLabel := StyleCompactLabel(labelColor).Render(label)
styledContent := StyleCompactContent(contentColor).Render(content)
return fmt.Sprintf("%s %-8s %s", styledSymbol, styledLabel, styledContent)
}
+12 -12
View File
@@ -413,12 +413,12 @@ func (r *MessageRenderer) formatToolResult(toolName, result string, width int) s
// Format bash/command output with better formatting
if strings.Contains(toolName, "bash") || strings.Contains(toolName, "command") {
theme := getTheme()
// Split result into sections if it contains both stdout and stderr
if strings.Contains(result, "<stdout>") || strings.Contains(result, "<stderr>") {
return r.formatBashOutput(result, width, theme)
}
// For simple output, just render as monospace text with proper line breaks
return baseStyle.
Width(width).
@@ -437,12 +437,12 @@ func (r *MessageRenderer) formatToolResult(toolName, result string, width int) s
// formatBashOutput formats bash command output with proper section handling
func (r *MessageRenderer) formatBashOutput(result string, width int, theme Theme) string {
baseStyle := lipgloss.NewStyle()
// Parse the output sections
lines := strings.Split(result, "\n")
var formattedLines []string
currentSection := ""
for _, line := range lines {
if strings.HasPrefix(line, "<stdout>") {
currentSection = "stdout"
@@ -466,7 +466,7 @@ func (r *MessageRenderer) formatBashOutput(result string, width int, theme Theme
formattedLines = append(formattedLines, "")
continue
}
// Regular content line
if currentSection == "stderr" {
styledLine := baseStyle.Foreground(theme.Error).Render(line)
@@ -476,7 +476,7 @@ func (r *MessageRenderer) formatBashOutput(result string, width int, theme Theme
formattedLines = append(formattedLines, line)
}
}
return baseStyle.
Width(width).
Foreground(theme.Muted).
@@ -519,7 +519,7 @@ type MessageContainer struct {
messages []UIMessage
width int
height int
compactMode bool // Add compact mode flag
compactMode bool // Add compact mode flag
modelName string // Store current model name
}
@@ -690,27 +690,27 @@ func (c *MessageContainer) renderEmptyState() string {
// renderCompactMessages renders messages in compact format
func (c *MessageContainer) renderCompactMessages() string {
var lines []string
for _, msg := range c.messages {
lines = append(lines, msg.Content)
}
return strings.Join(lines, "\n") + "\n"
}
// renderCompactEmptyState renders a simple empty state for compact mode
func (c *MessageContainer) renderCompactEmptyState() string {
theme := getTheme()
// Simple compact welcome
welcome := lipgloss.NewStyle().
Foreground(theme.System).
Bold(true).
Render("MCPHost - AI Assistant with MCP Tools")
help := lipgloss.NewStyle().
Foreground(theme.Muted).
Render("Type your message or /help for commands")
return fmt.Sprintf("%s\n%s\n\n", welcome, help)
}