Răsfoiți Sursa

feat: show sender name and timestamp

adamdottv 9 luni în urmă
părinte
comite
874715838a
2 a modificat fișierele cu 67 adăugiri și 20 ștergeri
  1. 19 0
      internal/config/config.go
  2. 48 20
      internal/tui/components/chat/message.go

+ 19 - 0
internal/config/config.go

@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"log/slog"
 	"os"
+	"os/user"
 	"path/filepath"
 	"strings"
 
@@ -712,6 +713,24 @@ func WorkingDirectory() string {
 	return cfg.WorkingDir
 }
 
+// GetHostname returns the system hostname or "User" if it can't be determined
+func GetHostname() (string, error) {
+	hostname, err := os.Hostname()
+	if err != nil {
+		return "User", err
+	}
+	return hostname, nil
+}
+
+// GetUsername returns the current user's username
+func GetUsername() (string, error) {
+	currentUser, err := user.Current()
+	if err != nil {
+		return "User", err
+	}
+	return currentUser.Username, nil
+}
+
 func UpdateAgentModel(agentName AgentName, modelID models.ModelID) error {
 	if cfg == nil {
 		panic("config not loaded")

+ 48 - 20
internal/tui/components/chat/message.go

@@ -82,7 +82,8 @@ func renderMessage(msg string, isUser bool, isFocused bool, width int, info ...s
 func renderUserMessage(msg message.Message, isFocused bool, width int, position int) uiMessage {
 	var styledAttachments []string
 	t := theme.CurrentTheme()
-	attachmentStyles := styles.BaseStyle().
+	baseStyle := styles.BaseStyle()
+	attachmentStyles := baseStyle.
 		MarginLeft(1).
 		Background(t.TextMuted()).
 		Foreground(t.Text())
@@ -96,12 +97,23 @@ func renderUserMessage(msg message.Message, isFocused bool, width int, position
 		}
 		styledAttachments = append(styledAttachments, attachmentStyles.Render(filename))
 	}
+
+	// Add timestamp info
+	info := []string{}
+	timestamp := time.Unix(msg.CreatedAt, 0).Format("15:04:05")
+	username, _ := config.GetUsername()
+	info = append(info, baseStyle.
+		Width(width-1).
+		Foreground(t.TextMuted()).
+		Render(fmt.Sprintf(" %s (%s)", username, timestamp)),
+	)
+
 	content := ""
 	if len(styledAttachments) > 0 {
-		attachmentContent := styles.BaseStyle().Width(width).Render(lipgloss.JoinHorizontal(lipgloss.Left, styledAttachments...))
-		content = renderMessage(msg.Content().String(), true, isFocused, width, attachmentContent)
+		attachmentContent := baseStyle.Width(width).Render(lipgloss.JoinHorizontal(lipgloss.Left, styledAttachments...))
+		content = renderMessage(msg.Content().String(), true, isFocused, width, append(info, attachmentContent)...)
 	} else {
-		content = renderMessage(msg.Content().String(), true, isFocused, width)
+		content = renderMessage(msg.Content().String(), true, isFocused, width, info...)
 	}
 	userMsg := uiMessage{
 		ID:          msg.ID,
@@ -134,36 +146,43 @@ func renderAssistantMessage(
 	t := theme.CurrentTheme()
 	baseStyle := styles.BaseStyle()
 
-	// Add finish info if available
+	// Always add timestamp info
+	timestamp := time.Unix(msg.CreatedAt, 0).Format("15:04:05")
+	modelName := "Assistant"
+	if msg.Model != "" {
+		modelName = models.SupportedModels[msg.Model].Name
+	}
+
+	info = append(info, baseStyle.
+		Width(width-1).
+		Foreground(t.TextMuted()).
+		Render(fmt.Sprintf(" %s (%s)", modelName, timestamp)),
+	)
+
 	if finished {
+		// Add finish info if available
 		switch finishData.Reason {
-		case message.FinishReasonEndTurn:
-			took := formatTimestampDiff(msg.CreatedAt, finishData.Time)
-			info = append(info, baseStyle.
-				Width(width-1).
-				Foreground(t.TextMuted()).
-				Render(fmt.Sprintf(" %s (%s)", models.SupportedModels[msg.Model].Name, took)),
-			)
 		case message.FinishReasonCanceled:
 			info = append(info, baseStyle.
 				Width(width-1).
-				Foreground(t.TextMuted()).
-				Render(fmt.Sprintf(" %s (%s)", models.SupportedModels[msg.Model].Name, "canceled")),
+				Foreground(t.Warning()).
+				Render("(canceled)"),
 			)
 		case message.FinishReasonError:
 			info = append(info, baseStyle.
 				Width(width-1).
-				Foreground(t.TextMuted()).
-				Render(fmt.Sprintf(" %s (%s)", models.SupportedModels[msg.Model].Name, "error")),
+				Foreground(t.Error()).
+				Render("(error)"),
 			)
 		case message.FinishReasonPermissionDenied:
 			info = append(info, baseStyle.
 				Width(width-1).
-				Foreground(t.TextMuted()).
-				Render(fmt.Sprintf(" %s (%s)", models.SupportedModels[msg.Model].Name, "permission denied")),
+				Foreground(t.Info()).
+				Render("(permission denied)"),
 			)
 		}
 	}
+
 	if content != "" || (finished && finishData.Reason == message.FinishReasonEndTurn) {
 		if content == "" {
 			content = "*Finished without output*"
@@ -180,8 +199,17 @@ func renderAssistantMessage(
 		position += messages[0].height
 		position++ // for the space
 	} else if thinking && thinkingContent != "" {
-		// Render the thinking content
-		content = renderMessage(thinkingContent, false, msg.ID == focusedUIMessageId, width)
+		// Render the thinking content with timestamp
+		content = renderMessage(thinkingContent, false, msg.ID == focusedUIMessageId, width, info...)
+		messages = append(messages, uiMessage{
+			ID:          msg.ID,
+			messageType: assistantMessageType,
+			position:    position,
+			height:      lipgloss.Height(content),
+			content:     content,
+		})
+		position += lipgloss.Height(content)
+		position++ // for the space
 	}
 
 	for i, toolCall := range msg.ToolCalls() {