feat: show sender name and timestamp
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -712,6 +713,24 @@ func WorkingDirectory() string {
|
|||||||
return cfg.WorkingDir
|
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 {
|
func UpdateAgentModel(agentName AgentName, modelID models.ModelID) error {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
panic("config not loaded")
|
panic("config not loaded")
|
||||||
|
|||||||
@@ -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 {
|
func renderUserMessage(msg message.Message, isFocused bool, width int, position int) uiMessage {
|
||||||
var styledAttachments []string
|
var styledAttachments []string
|
||||||
t := theme.CurrentTheme()
|
t := theme.CurrentTheme()
|
||||||
attachmentStyles := styles.BaseStyle().
|
baseStyle := styles.BaseStyle()
|
||||||
|
attachmentStyles := baseStyle.
|
||||||
MarginLeft(1).
|
MarginLeft(1).
|
||||||
Background(t.TextMuted()).
|
Background(t.TextMuted()).
|
||||||
Foreground(t.Text())
|
Foreground(t.Text())
|
||||||
@@ -96,12 +97,23 @@ func renderUserMessage(msg message.Message, isFocused bool, width int, position
|
|||||||
}
|
}
|
||||||
styledAttachments = append(styledAttachments, attachmentStyles.Render(filename))
|
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 := ""
|
content := ""
|
||||||
if len(styledAttachments) > 0 {
|
if len(styledAttachments) > 0 {
|
||||||
attachmentContent := styles.BaseStyle().Width(width).Render(lipgloss.JoinHorizontal(lipgloss.Left, styledAttachments...))
|
attachmentContent := baseStyle.Width(width).Render(lipgloss.JoinHorizontal(lipgloss.Left, styledAttachments...))
|
||||||
content = renderMessage(msg.Content().String(), true, isFocused, width, attachmentContent)
|
content = renderMessage(msg.Content().String(), true, isFocused, width, append(info, attachmentContent)...)
|
||||||
} else {
|
} else {
|
||||||
content = renderMessage(msg.Content().String(), true, isFocused, width)
|
content = renderMessage(msg.Content().String(), true, isFocused, width, info...)
|
||||||
}
|
}
|
||||||
userMsg := uiMessage{
|
userMsg := uiMessage{
|
||||||
ID: msg.ID,
|
ID: msg.ID,
|
||||||
@@ -134,36 +146,43 @@ func renderAssistantMessage(
|
|||||||
t := theme.CurrentTheme()
|
t := theme.CurrentTheme()
|
||||||
baseStyle := styles.BaseStyle()
|
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 {
|
if finished {
|
||||||
|
// Add finish info if available
|
||||||
switch finishData.Reason {
|
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:
|
case message.FinishReasonCanceled:
|
||||||
info = append(info, baseStyle.
|
info = append(info, baseStyle.
|
||||||
Width(width-1).
|
Width(width-1).
|
||||||
Foreground(t.TextMuted()).
|
Foreground(t.Warning()).
|
||||||
Render(fmt.Sprintf(" %s (%s)", models.SupportedModels[msg.Model].Name, "canceled")),
|
Render("(canceled)"),
|
||||||
)
|
)
|
||||||
case message.FinishReasonError:
|
case message.FinishReasonError:
|
||||||
info = append(info, baseStyle.
|
info = append(info, baseStyle.
|
||||||
Width(width-1).
|
Width(width-1).
|
||||||
Foreground(t.TextMuted()).
|
Foreground(t.Error()).
|
||||||
Render(fmt.Sprintf(" %s (%s)", models.SupportedModels[msg.Model].Name, "error")),
|
Render("(error)"),
|
||||||
)
|
)
|
||||||
case message.FinishReasonPermissionDenied:
|
case message.FinishReasonPermissionDenied:
|
||||||
info = append(info, baseStyle.
|
info = append(info, baseStyle.
|
||||||
Width(width-1).
|
Width(width-1).
|
||||||
Foreground(t.TextMuted()).
|
Foreground(t.Info()).
|
||||||
Render(fmt.Sprintf(" %s (%s)", models.SupportedModels[msg.Model].Name, "permission denied")),
|
Render("(permission denied)"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if content != "" || (finished && finishData.Reason == message.FinishReasonEndTurn) {
|
if content != "" || (finished && finishData.Reason == message.FinishReasonEndTurn) {
|
||||||
if content == "" {
|
if content == "" {
|
||||||
content = "*Finished without output*"
|
content = "*Finished without output*"
|
||||||
@@ -180,8 +199,17 @@ func renderAssistantMessage(
|
|||||||
position += messages[0].height
|
position += messages[0].height
|
||||||
position++ // for the space
|
position++ // for the space
|
||||||
} else if thinking && thinkingContent != "" {
|
} else if thinking && thinkingContent != "" {
|
||||||
// Render the thinking content
|
// Render the thinking content with timestamp
|
||||||
content = renderMessage(thinkingContent, false, msg.ID == focusedUIMessageId, width)
|
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() {
|
for i, toolCall := range msg.ToolCalls() {
|
||||||
|
|||||||
Reference in New Issue
Block a user