From f6e881b064e0cfec382fc7fc08af334d64473849 Mon Sep 17 00:00:00 2001 From: Castor Gemini Date: Fri, 22 Aug 2025 03:26:39 -0500 Subject: [PATCH] refactor(playback): Move formatting logic to prettyFormat.go - Create a new prettyFormat.go file to contain all the detailed log formatting and printing logic. - Simplify doPlayback.go to handle the summary view and call out to the new prettyFormatChat function for detailed views. - This separation of concerns makes the code cleaner and fixes compilation errors related to redeclared functions. --- doPlayback.go | 17 +++---- prettyFormat.go | 131 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 11 deletions(-) create mode 100644 prettyFormat.go diff --git a/doPlayback.go b/doPlayback.go index 94fd82d..9ece30e 100644 --- a/doPlayback.go +++ b/doPlayback.go @@ -11,25 +11,20 @@ func doPlayback() { showChat(argv.Playback.Uuid) return } + log.Infof("Found %d chat topic(s) in the log.", len(me.chats.GetChats())) fmt.Println("-------------------------------------------------") - // Iterate through the top-level Chat messages, which are now named groups. for _, chat := range me.chats.GetChats() { - - // Get the number of entries in the chat. entryCount := len(chat.GetEntries()) - - // Get the timestamp of the first entry to represent the chat's start time. var formattedTime string - if entryCount > 0 && chat.GetEntries()[0].GetCtime() != nil { - t := chat.GetEntries()[0].GetCtime().AsTime() - formattedTime = t.Format("2006-01-02 15:04:05") // YYYY-MM-DD HH:MM:SS + if ctime := chat.GetCtime(); ctime != nil { + t := ctime.AsTime() + formattedTime = t.Format("2006-01-02 15:04:05") } else { formattedTime = "No Timestamp" } - // Print the formatted one-line summary. fmt.Printf("Topic: %-25s | Entries: %-4d | Started: %s | UUID: %s\n", chat.GetChatName(), entryCount, @@ -46,6 +41,6 @@ func showChat(uuid string) { log.Info("unknown uuid", uuid) return } - log.Info("uuid was found ok", uuid) - // TODO: show the chat entries here + // Call the new, dedicated formatting function. + prettyFormatChat(chat) } diff --git a/prettyFormat.go b/prettyFormat.go new file mode 100644 index 0000000..e898929 --- /dev/null +++ b/prettyFormat.go @@ -0,0 +1,131 @@ +package main + +import ( + "fmt" + "os" + "path/filepath" + "strings" + + "go.wit.com/lib/protobuf/chatpb" +) + +const termWidth = 120 // 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) { + fmt.Printf("\n========================================================\n") + fmt.Printf("== Chat Topic: %s (UUID: %s)\n", chat.GetChatName(), chat.GetUuid()) + fmt.Printf("========================================================\n\n") + + // Since content files are relative, we need a path to a log file. + // We can reconstruct the config path using the same logic as ConfigSave/ConfigLoad. + logDir := os.Getenv("GEMINI_HOME") + if logDir == "" { + homeDir, _ := os.UserHomeDir() + logDir = filepath.Join(homeDir, ".config/gemini") + } + + 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" + } + + var content string + if contentFile := entry.GetContentFile(); contentFile != "" { + contentPath := filepath.Join(logDir, contentFile) + contentBytes, err := os.ReadFile(contentPath) + if err != nil { + content = fmt.Sprintf("--- ERROR: Could not read content file %s: %v ---", contentPath, err) + } else { + content = string(contentBytes) + } + } else { + content = entry.GetContent() + } + + if content != "" { + fmt.Printf("✦ %s (%s):\n%s\n", author, formattedTime, strings.TrimSpace(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, logDir) + } + } + fmt.Println() + } +} + +func printTable(table *chatpb.Table) { + if table == nil || len(table.GetRows()) == 0 { + return + } + fmt.Println("┌─[ Table Data ]──────────────────────────────────────────") + for _, row := range table.GetRows() { + fmt.Printf("│ %s\n", strings.Join(row.GetFields(), " │ ")) + } + fmt.Printf("└─────────────────────────────────────────────────────────\n\n") +} + +func printCodeSnippet(snippet *chatpb.CodeSnippet, logDir string) { + snippetPath := filepath.Join(logDir, snippet.GetFilename()) + codeBytes, err := os.ReadFile(snippetPath) + if err != nil { + fmt.Printf("┌─[ ERROR: Could not read snippet file %s ]─\n\n", snippetPath) + return + } + code := string(codeBytes) + language := filepath.Base(snippet.GetFilename()) + fmt.Printf("┌─[ Code Snippet: %s ]──────────────────────────────────\n", language) + for _, line := range strings.Split(strings.TrimSpace(code), "\n") { + fmt.Printf("│ %s\n", line) + } + fmt.Printf("└─────────────────────────────────────────────────────────\n\n") +} + +func printToolCallBox(tc *chatpb.ToolCall) { + boxWidth := termWidth - 2 + fmt.Printf(" ╭%s╮\n", strings.Repeat("─", boxWidth)) + header := fmt.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) + fmt.Printf(" ╰%s╯\n", strings.Repeat("─", boxWidth)) +} + +func printWrappedLine(text string, width int) { + if len(text) == 0 { + printEmptyLine(width) + return + } + for len(text) > width { + fmt.Printf(" │ %-*s │\n", width, text[:width]) + text = text[width:] + } + fmt.Printf(" │ %-*s │\n", width, text) +} + +func printEmptyLine(width int) { + fmt.Printf(" │ %*s │\n", width, "") +}