// This file contains functions for formatting and printing chat logs to the terminal. // These functions operate exclusively on the in-memory protobuf data structures. // They do NOT access the filesystem; all content (from content files, snippets, etc.) // is expected to have been loaded into the 'Content' fields of the protobuf messages // before these functions are called. package main import ( "path/filepath" "strings" "go.wit.com/lib/protobuf/chatpb" "go.wit.com/log" ) const termWidth = 100 // The target width for the formatted output boxes. // prettyFormatChat is the main entry point to print a detailed view of a Chat topic. func prettyFormatChat(chat *chatpb.Chat) { log.Printf("\n========================================================\n") log.Printf("== Chat Topic: %s (UUID: %s)\n", chat.GetChatName(), chat.GetUuid()) log.Printf("========================================================\n\n") for _, entry := range chat.GetEntries() { author := entry.GetFrom().String() var formattedTime string if ctime := entry.GetCtime(); ctime != nil { t := ctime.AsTime() formattedTime = t.Format("2006-01-02 15:04:05") } else { formattedTime = "No Timestamp" } // Use the in-memory Content field directly. content := entry.GetContent() if content != "" { printContent(author, formattedTime, content) } if table := entry.GetTable(); table != nil { printTable(table) } for _, toolCall := range entry.GetToolCalls() { printToolCallBox(toolCall) } if snippets := entry.GetSnippets(); snippets != nil { for _, snippet := range snippets { printCodeSnippet(snippet) } } log.Println() } } // printContent handles the wrapping for the main conversational text. func printContent(author, timestamp, content string) { // Right-align USER messages, left-align REGEX messages. if author == "USER" { printRightAligned(author, timestamp, content) } else { printLeftAligned(author, timestamp, content) } } func printLeftAligned(author, timestamp, content string) { prefix := log.Sprintf("✦ %s (%s):", author, timestamp) log.Println(prefix) indent := "\t" contentWidth := termWidth - 8 // 8 spaces for a standard tab paragraphs := strings.Split(content, "\n") for _, paragraph := range paragraphs { words := strings.Fields(paragraph) if len(words) == 0 { log.Println() // Preserve paragraph breaks continue } currentLine := indent + words[0] for _, word := range words[1:] { if len(currentLine)+1+len(word) <= contentWidth { currentLine += " " + word } else { log.Println(currentLine) currentLine = indent + word } } log.Println(currentLine) } } func printRightAligned(author, timestamp, content string) { prefix := log.Sprintf("(%s) %s ✦", timestamp, author) // Print the prefix first, right-aligned. log.Printf("%*s\n", termWidth, prefix) // The available width for the text. contentWidth := termWidth - 8 // Leave a tab's worth of margin on the left paragraphs := strings.Split(content, "\n") for _, paragraph := range paragraphs { words := strings.Fields(paragraph) if len(words) == 0 { log.Println() // Preserve paragraph breaks continue } currentLine := words[0] for _, word := range words[1:] { if len(currentLine)+1+len(word) <= contentWidth { currentLine += " " + word } else { // Print the completed line, right-aligned. log.Printf("%*s\n", termWidth, currentLine) currentLine = word } } // Print the last remaining line of the paragraph, right-aligned. log.Printf("%*s\n", termWidth, currentLine) } } func printTable(table *chatpb.Table) { if table == nil || len(table.GetRows()) == 0 { return } log.Println("┌─[ Table Data ]──────────────────────────────────────────") for _, row := range table.GetRows() { log.Printf("│ %s\n", strings.Join(row.GetFields(), " │ ")) } log.Printf("└─────────────────────────────────────────────────────────\n\n") } func printCodeSnippet(snippet *chatpb.CodeSnippet) { // Use the in-memory Content field directly. code := snippet.GetContent() language := filepath.Base(snippet.GetFilename()) // Still useful for display log.Println() // Add extra line feed for spacing // --- Top Border --- topBorder := log.Sprintf("┌─[ Code Snippet: %s ]", language) log.Printf("%s%s┐\n", topBorder, strings.Repeat("─", termWidth-len(topBorder)-1)) // --- Content Lines --- for _, line := range strings.Split(strings.TrimSpace(code), "\n") { // Calculate padding: total width - borders/padding - text length padding := termWidth - 4 - len(line) if padding < 0 { padding = 0 // Should not happen with wrapping, but as a safeguard } log.Printf("│ %s%s │\n", line, strings.Repeat(" ", padding)) } // --- Bottom Border --- log.Printf("└%s┘\n\n", strings.Repeat("─", termWidth-2)) } func printToolCallBox(tc *chatpb.ToolCall) { boxWidth := termWidth - 2 log.Printf(" ╭%s╮\n", strings.Repeat("─", boxWidth)) header := log.Sprintf(" ✔ %s %s (%s)", tc.GetName(), tc.GetInput(), tc.GetDescription()) printWrappedLine(header, boxWidth) printEmptyLine(boxWidth) if stdout := tc.GetOutputStdout(); stdout != "" { for _, line := range strings.Split(stdout, "\n") { printWrappedLine(" "+line, boxWidth) } } if stderr := tc.GetOutputStderr(); stderr != "" { for _, line := range strings.Split(stderr, "\n") { printWrappedLine(" "+line, boxWidth) } } printEmptyLine(boxWidth) log.Printf(" ╰%s╯\n", strings.Repeat("─", boxWidth)) } func printWrappedLine(text string, width int) { if len(text) == 0 { printEmptyLine(width) return } for len(text) > width { log.Printf(" │ %-*s │\n", width, text[:width]) text = text[width:] } log.Printf(" │ %-*s │\n", width, text) } func printEmptyLine(width int) { log.Printf(" │ %*s │\n", width, "") }