summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authoradamdottv <[email protected]>2025-05-05 11:02:02 -0500
committeradamdottv <[email protected]>2025-05-05 11:02:02 -0500
commit874715838af25915061048ac20ea536363d62fb7 (patch)
tree87428b0d291097ff2efd5724821fa231bf88d032
parent167eb9ddfa366f38b72362624c4b28ac587cfce6 (diff)
downloadopencode-874715838af25915061048ac20ea536363d62fb7.tar.gz
opencode-874715838af25915061048ac20ea536363d62fb7.zip
feat: show sender name and timestamp
-rw-r--r--internal/config/config.go19
-rw-r--r--internal/tui/components/chat/message.go68
2 files changed, 67 insertions, 20 deletions
diff --git a/internal/config/config.go b/internal/config/config.go
index 06e996f05..745d88d8e 100644
--- a/internal/config/config.go
+++ b/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")
diff --git a/internal/tui/components/chat/message.go b/internal/tui/components/chat/message.go
index 6711ab525..64006113d 100644
--- a/internal/tui/components/chat/message.go
+++ b/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() {