200 lines
5.9 KiB
Go
200 lines
5.9 KiB
Go
// 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, "")
|
|
}
|