summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authoradamdottv <[email protected]>2025-06-02 11:32:58 -0500
committeradamdottv <[email protected]>2025-06-02 11:33:01 -0500
commitda92ee5f0981b6f68c0e846f226ca2d8cadaa386 (patch)
treeccdcb9e165a61bc1d5a40dbccc3eaf2dc658cdab
parent80de5d489f460df491f51f881061729220df47ce (diff)
downloadopencode-da92ee5f0981b6f68c0e846f226ca2d8cadaa386.tar.gz
opencode-da92ee5f0981b6f68c0e846f226ca2d8cadaa386.zip
wip: refactoring tui
-rw-r--r--packages/tui/cmd/root.go14
-rw-r--r--packages/tui/internal/tui/components/chat/message.go250
2 files changed, 104 insertions, 160 deletions
diff --git a/packages/tui/cmd/root.go b/packages/tui/cmd/root.go
index 85258d591..175f50b3c 100644
--- a/packages/tui/cmd/root.go
+++ b/packages/tui/cmd/root.go
@@ -37,13 +37,13 @@ to assist developers in writing, debugging, and understanding code directly from
}
// Setup logging
- file, err := os.OpenFile("app.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
- if err != nil {
- panic(err)
- }
- defer file.Close()
- logger := slog.New(slog.NewTextHandler(file, &slog.HandlerOptions{Level: slog.LevelDebug}))
- slog.SetDefault(logger)
+ // file, err := os.OpenFile("debug.log", os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
+ // if err != nil {
+ // panic(err)
+ // }
+ // defer file.Close()
+ // logger := slog.New(slog.NewTextHandler(file, &slog.HandlerOptions{Level: slog.LevelDebug}))
+ // slog.SetDefault(logger)
// Load the config
debug, _ := cmd.Flags().GetBool("debug")
diff --git a/packages/tui/internal/tui/components/chat/message.go b/packages/tui/internal/tui/components/chat/message.go
index c86cc45d1..e04572f07 100644
--- a/packages/tui/internal/tui/components/chat/message.go
+++ b/packages/tui/internal/tui/components/chat/message.go
@@ -35,7 +35,6 @@ func renderUserMessage(msg client.MessageInfo, width int) string {
BorderForeground(t.Secondary()).
BorderStyle(lipgloss.ThickBorder())
- baseStyle := styles.BaseStyle()
// var styledAttachments []string
// attachmentStyles := baseStyle.
// MarginLeft(1).
@@ -52,12 +51,14 @@ func renderUserMessage(msg client.MessageInfo, width int) string {
// styledAttachments = append(styledAttachments, attachmentStyles.Render(filename))
// }
- // Add timestamp info
timestamp := time.UnixMilli(int64(msg.Metadata.Time.Created)).Local().Format("02 Jan 2006 03:04 PM")
+ if time.Now().Format("02 Jan 2006") == timestamp[:11] {
+ timestamp = timestamp[12:]
+ }
username, _ := config.GetUsername()
- info := baseStyle.
+ info := styles.Padded().
Foreground(t.TextMuted()).
- Render(fmt.Sprintf(" %s (%s)", username, timestamp))
+ Render(fmt.Sprintf("%s (%s)", username, timestamp))
content := ""
// if len(styledAttachments) > 0 {
@@ -101,27 +102,16 @@ func renderAssistantMessage(
Foreground(t.TextMuted()).
BorderForeground(t.Primary()).
BorderStyle(lipgloss.ThickBorder())
- toolStyle := styles.BaseStyle().
- BorderLeft(true).
- Foreground(t.TextMuted()).
- BorderForeground(t.TextMuted()).
- BorderStyle(lipgloss.ThickBorder())
-
- baseStyle := styles.BaseStyle()
messages := []string{}
- // content := strings.TrimSpace(msg.Content().String())
- // thinking := msg.IsThinking()
- // thinkingContent := msg.ReasoningContent().Thinking
- // finished := msg.IsFinished()
- // finishData := msg.FinishPart()
-
- // Add timestamp info
timestamp := time.UnixMilli(int64(msg.Metadata.Time.Created)).Local().Format("02 Jan 2006 03:04 PM")
+ if time.Now().Format("02 Jan 2006") == timestamp[:11] {
+ timestamp = timestamp[12:]
+ }
modelName := msg.Metadata.Assistant.ModelID
- info := baseStyle.
+ info := styles.Padded().
Foreground(t.TextMuted()).
- Render(fmt.Sprintf(" %s (%s)", modelName, timestamp))
+ Render(fmt.Sprintf("%s (%s)", modelName, timestamp))
for _, p := range msg.Parts {
part, err := p.ValueByDiscriminator()
@@ -130,6 +120,9 @@ func renderAssistantMessage(
}
switch part.(type) {
+ // case client.MessagePartReasoning:
+ // reasoningPart := part.(client.MessagePartReasoning)
+
case client.MessagePartText:
textPart := part.(client.MessagePartText)
text := toMarkdown(textPart.Text, width)
@@ -143,155 +136,106 @@ func renderAssistantMessage(
}
toolInvocationPart := part.(client.MessagePartToolInvocation)
- toolInvocation, _ := toolInvocationPart.ToolInvocation.ValueByDiscriminator()
- switch toolInvocation.(type) {
- case client.MessageToolInvocationToolCall:
- toolCall := toolInvocation.(client.MessageToolInvocationToolCall)
- toolName := renderToolName(toolCall.ToolName)
-
- var toolArgs []string
- toolMap, _ := convertToMap(toolCall.Args)
- for _, arg := range toolMap {
- toolArgs = append(toolArgs, fmt.Sprintf("%v", arg))
- }
- params := renderParams(width-lipgloss.Width(toolName)-1, toolArgs...)
- title := styles.Padded().Render(fmt.Sprintf("%s: %s", toolName, params))
-
- content := toolStyle.Render(lipgloss.JoinVertical(lipgloss.Left,
- title,
- " In progress...",
- ))
- message := styles.ForceReplaceBackgroundWithLipgloss(content, t.Background())
- messages = append(messages, message)
-
- case client.MessageToolInvocationToolResult:
- toolInvocationResult := toolInvocation.(client.MessageToolInvocationToolResult)
- toolName := renderToolName(toolInvocationResult.ToolName)
- var toolArgs []string
- toolMap, _ := convertToMap(toolInvocationResult.Args)
- for _, arg := range toolMap {
- toolArgs = append(toolArgs, fmt.Sprintf("%v", arg))
- }
- params := renderParams(width-lipgloss.Width(toolName)-1, toolArgs...)
- title := styles.Padded().Render(fmt.Sprintf("%s: %s", toolName, params))
- metadata := msg.Metadata.Tool[toolInvocationResult.ToolCallId].(map[string]any)
-
- var markdown string
- if toolInvocationResult.ToolName == "opencode_edit" {
- filename := toolMap["filePath"].(string)
- title = styles.Padded().Render(fmt.Sprintf("%s: %s", toolName, filename))
- oldString := toolMap["oldString"].(string)
- newString := toolMap["newString"].(string)
- patch, _, _ := diff.GenerateDiff(oldString, newString, filename)
- formattedDiff, _ := diff.FormatDiff(patch, diff.WithTotalWidth(width))
- markdown = strings.TrimSpace(formattedDiff)
- message := toolStyle.Render(lipgloss.JoinVertical(lipgloss.Left,
- title,
- markdown,
- ))
- messages = append(messages, message)
- } else if toolInvocationResult.ToolName == "view" {
- result := toolInvocationResult.Result
- if metadata["preview"] != nil {
- result = metadata["preview"].(string)
- }
- filename := toolMap["filePath"].(string)
- ext := filepath.Ext(filename)
- if ext == "" {
- ext = ""
- } else {
- ext = strings.ToLower(ext[1:])
- }
- result = fmt.Sprintf("```%s\n%s\n```", ext, truncateHeight(result, 10))
- markdown = toMarkdown(result, width)
- content := toolStyle.Render(lipgloss.JoinVertical(lipgloss.Left,
- title,
- markdown,
- ))
- message := styles.ForceReplaceBackgroundWithLipgloss(content, t.Background())
- messages = append(messages, message)
- } else {
- result := truncateHeight(strings.TrimSpace(toolInvocationResult.Result), 10)
- markdown = toMarkdown(result, width)
- content := toolStyle.Render(lipgloss.JoinVertical(lipgloss.Left,
- title,
- markdown,
- ))
- message := styles.ForceReplaceBackgroundWithLipgloss(content, t.Background())
- messages = append(messages, message)
- }
+ toolCall, _ := toolInvocationPart.ToolInvocation.AsMessageToolInvocationToolCall()
+ var result *string
+ resultPart, resultError := toolInvocationPart.ToolInvocation.AsMessageToolInvocationToolResult()
+ if resultError == nil {
+ result = &resultPart.Result
}
+ metadata := map[string]any{}
+ if _, ok := msg.Metadata.Tool[toolCall.ToolCallId]; ok {
+ metadata = msg.Metadata.Tool[toolCall.ToolCallId].(map[string]any)
+ }
+ message := renderToolInvocation(toolCall, result, metadata, width)
+ messages = append(messages, message)
}
}
- // if finished {
- // // Add finish info if available
- // switch finishData.Reason {
- // case message.FinishReasonCanceled:
- // info = append(info, baseStyle.
- // Width(width-1).
- // Foreground(t.Warning()).
- // Render("(canceled)"),
- // )
- // case message.FinishReasonError:
- // info = append(info, baseStyle.
- // Width(width-1).
- // Foreground(t.Error()).
- // Render("(error)"),
- // )
- // case message.FinishReasonPermissionDenied:
- // info = append(info, baseStyle.
- // Width(width-1).
- // Foreground(t.Info()).
- // Render("(permission denied)"),
- // )
- // }
- // }
+ return strings.Join(messages, "\n\n")
+}
- // if content != "" || (finished && finishData.Reason == message.FinishReasonEndTurn) {
- // if content == "" {
- // content = "*Finished without output*"
- // }
- //
- // content = renderMessage(content, false, width, info...)
- // messages = append(messages, content)
- // // position += messages[0].height
- // position++ // for the space
- // } else if thinking && thinkingContent != "" {
- // // Render the thinking content with timestamp
- // content = renderMessage(thinkingContent, false, width, info...)
- // messages = append(messages, content)
- // position += lipgloss.Height(content)
- // position++ // for the space
- // }
+func renderToolInvocation(toolCall client.MessageToolInvocationToolCall, result *string, metadata map[string]any, width int) string {
+ t := theme.CurrentTheme()
+ style := styles.BaseStyle().
+ BorderLeft(true).
+ Foreground(t.TextMuted()).
+ BorderForeground(t.TextMuted()).
+ BorderStyle(lipgloss.ThickBorder())
- // Only render tool messages if they should be shown
- if showToolMessages {
- // for i, toolCall := range msg.ToolCalls() {
- // toolCallContent := renderToolMessage(
- // toolCall,
- // allMessages,
- // messagesService,
- // focusedUIMessageId,
- // false,
- // width,
- // i+1,
- // )
- // messages = append(messages, toolCallContent)
- // }
+ toolName := renderToolName(toolCall.ToolName)
+ var toolArgs []string
+ toolMap, _ := convertToMap(toolCall.Args)
+ for _, arg := range toolMap {
+ toolArgs = append(toolArgs, fmt.Sprintf("%v", arg))
+ }
+ params := renderParams(width-lipgloss.Width(toolName)-1, toolArgs...)
+ title := styles.Padded().Render(fmt.Sprintf("%s: %s", toolName, params))
+ finished := result != nil
+ body := styles.Padded().Render("In progress...")
+ if finished {
+ body = *result
}
- return strings.Join(messages, "\n\n")
+ var markdown string
+ if toolCall.ToolName == "opencode_edit" {
+ filename := toolMap["filePath"].(string)
+ title = styles.Padded().Render(fmt.Sprintf("%s: %s", toolName, filename))
+ oldString := toolMap["oldString"].(string)
+ newString := toolMap["newString"].(string)
+ patch, _, _ := diff.GenerateDiff(oldString, newString, filename)
+ formattedDiff, _ := diff.FormatDiff(patch, diff.WithTotalWidth(width))
+ markdown = strings.TrimSpace(formattedDiff)
+ return style.Render(lipgloss.JoinVertical(lipgloss.Left,
+ title,
+ markdown,
+ ))
+ } else if toolCall.ToolName == "opencode_view" {
+ filename := toolMap["filePath"].(string)
+ ext := filepath.Ext(filename)
+ if ext == "" {
+ ext = ""
+ } else {
+ ext = strings.ToLower(ext[1:])
+ }
+ if finished {
+ if metadata["preview"] != nil {
+ body = metadata["preview"].(string)
+ }
+ body = fmt.Sprintf("```%s\n%s\n```", ext, truncateHeight(body, 10))
+ body = toMarkdown(body, width)
+ }
+ content := style.Render(lipgloss.JoinVertical(lipgloss.Left,
+ title,
+ body,
+ ))
+ return styles.ForceReplaceBackgroundWithLipgloss(content, t.Background())
+ }
+
+ // Default rendering
+ if finished {
+ body = truncateHeight(strings.TrimSpace(body), 10)
+ markdown = toMarkdown(body, width)
+ }
+ content := style.Render(lipgloss.JoinVertical(lipgloss.Left,
+ title,
+ body,
+ ))
+ return styles.ForceReplaceBackgroundWithLipgloss(content, t.Background())
}
func renderToolName(name string) string {
switch name {
// case agent.AgentToolName:
// return "Task"
- case "ls":
+ case "opencode_ls":
return "List"
default:
- return cases.Title(language.Und).String(name)
+ normalizedName := name
+ if strings.HasPrefix(name, "opencode_") {
+ normalizedName = strings.TrimPrefix(name, "opencode_")
+ }
+
+ return cases.Title(language.Und).String(normalizedName)
}
}