summaryrefslogtreecommitdiffhomepage
path: root/packages/tui/internal/components/chat
diff options
context:
space:
mode:
authoradamdottv <[email protected]>2025-06-13 15:56:30 -0500
committeradamdottv <[email protected]>2025-06-13 15:56:33 -0500
commit67023bb00710b6a40836800da2eb5cdacc1ee9c1 (patch)
tree4c6fce2842956f214b8e39a7465d4444b37d39b0 /packages/tui/internal/components/chat
parenta316aed4fe973682667a19e6ba550270cf1a9df4 (diff)
downloadopencode-67023bb00710b6a40836800da2eb5cdacc1ee9c1.tar.gz
opencode-67023bb00710b6a40836800da2eb5cdacc1ee9c1.zip
wip: refactoring tui
Diffstat (limited to 'packages/tui/internal/components/chat')
-rw-r--r--packages/tui/internal/components/chat/editor.go38
-rw-r--r--packages/tui/internal/components/chat/message.go114
-rw-r--r--packages/tui/internal/components/chat/messages.go3
3 files changed, 86 insertions, 69 deletions
diff --git a/packages/tui/internal/components/chat/editor.go b/packages/tui/internal/components/chat/editor.go
index 2e89a7db4..95ca3e14b 100644
--- a/packages/tui/internal/components/chat/editor.go
+++ b/packages/tui/internal/components/chat/editor.go
@@ -44,11 +44,6 @@ type EditorKeyMaps struct {
HistoryDown key.Binding
}
-type bluredEditorKeyMaps struct {
- Send key.Binding
- Focus key.Binding
- OpenEditor key.Binding
-}
type DeleteAttachmentKeyMaps struct {
AttachmentDeleteMode key.Binding
Escape key.Binding
@@ -108,10 +103,18 @@ func (m *editorComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case dialog.ThemeChangedMsg:
m.textarea = createTextArea(&m.textarea)
case dialog.CompletionSelectedMsg:
- existingValue := m.textarea.Value()
- modifiedValue := strings.Replace(existingValue, msg.SearchString, msg.CompletionValue, 1)
- m.textarea.SetValue(modifiedValue)
- return m, nil
+ if msg.IsCommand {
+ // Execute the command directly
+ commandName := strings.TrimPrefix(msg.CompletionValue, "/")
+ m.textarea.Reset()
+ return m, util.CmdHandler(commands.ExecuteCommandMsg{Name: commandName})
+ } else {
+ // For files, replace the text in the editor
+ existingValue := m.textarea.Value()
+ modifiedValue := strings.Replace(existingValue, msg.SearchString, msg.CompletionValue, 1)
+ m.textarea.SetValue(modifiedValue)
+ return m, nil
+ }
case tea.KeyMsg:
switch msg.String() {
case "ctrl+c":
@@ -378,12 +381,13 @@ func (m *editorComponent) send() tea.Cmd {
}
// Check for slash command
- if strings.HasPrefix(value, "/") {
- commandName := strings.TrimPrefix(value, "/")
- if _, ok := m.app.Commands[commandName]; ok {
- return util.CmdHandler(commands.ExecuteCommandMsg{Name: commandName})
- }
- }
+ // if strings.HasPrefix(value, "/") {
+ // commandName := strings.TrimPrefix(value, "/")
+ // if _, ok := m.app.Commands[commandName]; ok {
+ // return util.CmdHandler(commands.ExecuteCommandMsg{Name: commandName})
+ // }
+ // }
+ slog.Info("Send message", "value", value)
return tea.Batch(
util.CmdHandler(SendMsg{
@@ -452,6 +456,10 @@ func createTextArea(existing *textarea.Model) textarea.Model {
return ta
}
+func (m *editorComponent) GetValue() string {
+ return m.textarea.Value()
+}
+
func NewEditorComponent(app *app.App) layout.ModelWithView {
s := spinner.New(spinner.WithSpinner(spinner.Ellipsis), spinner.WithStyle(styles.Muted().Width(3)))
ta := createTextArea(nil)
diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go
index 44aceea93..3315d4ba3 100644
--- a/packages/tui/internal/components/chat/message.go
+++ b/packages/tui/internal/components/chat/message.go
@@ -216,7 +216,7 @@ func renderText(message client.MessageInfo, text string, author string) string {
align = lipgloss.Left
}
- textWidth := lipgloss.Width(text)
+ textWidth := max(lipgloss.Width(text), lipgloss.Width(info))
markdownWidth := min(textWidth, width-padding-4) // -4 for the border and padding
content := toMarkdown(text, markdownWidth, t.BackgroundSubtle())
content = lipgloss.JoinVertical(align, content, info)
@@ -299,13 +299,9 @@ func renderToolInvocation(
body := ""
error := ""
finished := result != nil && *result != ""
- if finished {
- body = *result
- }
if e, ok := metadata.Get("error"); ok && e.(bool) == true {
if m, ok := metadata.Get("message"); ok {
- body = "" // don't show the body if there's an error
style = style.BorderLeftForeground(t.Error())
error = styles.BaseStyle().
Background(t.BackgroundSubtle()).
@@ -336,58 +332,61 @@ func renderToolInvocation(
case "opencode_read":
toolArgs = renderArgs(&toolArgsMap, "filePath")
title = fmt.Sprintf("Read: %s %s", toolArgs, elapsed)
- body = ""
if preview, ok := metadata.Get("preview"); ok && toolArgsMap["filePath"] != nil {
filename := toolArgsMap["filePath"].(string)
body = preview.(string)
body = renderFile(filename, body, WithTruncate(6))
}
case "opencode_edit":
- filename := toolArgsMap["filePath"].(string)
- title = fmt.Sprintf("Edit: %s %s", relative(filename), elapsed)
- if d, ok := metadata.Get("diff"); ok {
- patch := d.(string)
- var formattedDiff string
- if layout.Current.Viewport.Width < 80 {
- formattedDiff, _ = diff.FormatUnifiedDiff(
- filename,
- patch,
- diff.WithWidth(layout.Current.Container.Width-2),
+ if filename, ok := toolArgsMap["filePath"].(string); ok {
+ title = fmt.Sprintf("Edit: %s %s", relative(filename), elapsed)
+ if d, ok := metadata.Get("diff"); ok {
+ patch := d.(string)
+ var formattedDiff string
+ if layout.Current.Viewport.Width < 80 {
+ formattedDiff, _ = diff.FormatUnifiedDiff(
+ filename,
+ patch,
+ diff.WithWidth(layout.Current.Container.Width-2),
+ )
+ } else {
+ diffWidth := min(layout.Current.Viewport.Width-2, 120)
+ formattedDiff, _ = diff.FormatDiff(filename, patch, diff.WithTotalWidth(diffWidth))
+ }
+ formattedDiff = strings.TrimSpace(formattedDiff)
+ formattedDiff = lipgloss.NewStyle().
+ BorderStyle(lipgloss.ThickBorder()).
+ BorderForeground(t.BackgroundSubtle()).
+ BorderLeft(true).
+ BorderRight(true).
+ Render(formattedDiff)
+
+ if showResult {
+ style = style.Width(lipgloss.Width(formattedDiff))
+ title += "\n"
+ }
+
+ body = strings.TrimSpace(formattedDiff)
+ body = lipgloss.Place(
+ layout.Current.Viewport.Width,
+ lipgloss.Height(body)+1,
+ lipgloss.Center,
+ lipgloss.Top,
+ body,
)
- } else {
- diffWidth := min(layout.Current.Viewport.Width-2, 120)
- formattedDiff, _ = diff.FormatDiff(filename, patch, diff.WithTotalWidth(diffWidth))
- }
- formattedDiff = strings.TrimSpace(formattedDiff)
- formattedDiff = lipgloss.NewStyle().
- BorderStyle(lipgloss.ThickBorder()).
- BorderForeground(t.BackgroundSubtle()).
- BorderLeft(true).
- BorderRight(true).
- Render(formattedDiff)
-
- if showResult {
- style = style.Width(lipgloss.Width(formattedDiff))
- title += "\n"
}
-
- body = strings.TrimSpace(formattedDiff)
- body = lipgloss.Place(
- layout.Current.Viewport.Width,
- lipgloss.Height(body)+1,
- lipgloss.Center,
- lipgloss.Top,
- body,
- )
}
case "opencode_write":
- filename := toolArgsMap["filePath"].(string)
- title = fmt.Sprintf("Write: %s %s", relative(filename), elapsed)
- content := toolArgsMap["content"].(string)
- body = renderFile(filename, content)
+ if filename, ok := toolArgsMap["filePath"].(string); ok {
+ title = fmt.Sprintf("Write: %s %s", relative(filename), elapsed)
+ if content, ok := toolArgsMap["content"].(string); ok {
+ body = renderFile(filename, content)
+ }
+ }
case "opencode_bash":
- description := toolArgsMap["description"].(string)
- title = fmt.Sprintf("Shell: %s %s", description, elapsed)
+ if description, ok := toolArgsMap["description"].(string); ok {
+ title = fmt.Sprintf("Shell: %s %s", description, elapsed)
+ }
if stdout, ok := metadata.Get("stdout"); ok {
command := toolArgsMap["command"].(string)
stdout := stdout.(string)
@@ -396,18 +395,20 @@ func renderToolInvocation(
body = renderContentBlock(body, WithFullWidth(), WithMarginBottom(1))
}
case "opencode_webfetch":
+ toolArgs = renderArgs(&toolArgsMap, "url")
title = fmt.Sprintf("Fetching: %s %s", toolArgs, elapsed)
- format := toolArgsMap["format"].(string)
- body = truncateHeight(body, 10)
- if format == "html" || format == "markdown" {
- body = toMarkdown(body, innerWidth, t.BackgroundSubtle())
+ if format, ok := toolArgsMap["format"].(string); ok {
+ body = *result
+ body = truncateHeight(body, 10)
+ if format == "html" || format == "markdown" {
+ body = toMarkdown(body, innerWidth, t.BackgroundSubtle())
+ }
+ body = renderContentBlock(body, WithFullWidth(), WithMarginBottom(1))
}
- body = renderContentBlock(body, WithFullWidth(), WithMarginBottom(1))
case "opencode_todowrite":
title = fmt.Sprintf("Planning %s", elapsed)
if to, ok := metadata.Get("todos"); ok && finished {
- body = ""
todos := to.([]any)
for _, todo := range todos {
t := todo.(map[string]any)
@@ -416,7 +417,7 @@ func renderToolInvocation(
case "completed":
body += fmt.Sprintf("- [x] %s\n", content)
// case "in-progress":
- // body += fmt.Sprintf("- [ ] _%s_\n", content)
+ // body += fmt.Sprintf("- [ ] %s\n", content)
default:
body += fmt.Sprintf("- [ ] %s\n", content)
}
@@ -427,6 +428,13 @@ func renderToolInvocation(
default:
toolName := renderToolName(toolCall.ToolName)
title = fmt.Sprintf("%s: %s %s", toolName, toolArgs, elapsed)
+ body = *result
+ body = truncateHeight(body, 10)
+ body = renderContentBlock(body, WithFullWidth(), WithMarginBottom(1))
+ }
+
+ if body == "" && error == "" {
+ body = *result
body = truncateHeight(body, 10)
body = renderContentBlock(body, WithFullWidth(), WithMarginBottom(1))
}
diff --git a/packages/tui/internal/components/chat/messages.go b/packages/tui/internal/components/chat/messages.go
index 8ab11a54a..83c3898a7 100644
--- a/packages/tui/internal/components/chat/messages.go
+++ b/packages/tui/internal/components/chat/messages.go
@@ -245,7 +245,7 @@ func (m *messagesComponent) header() string {
base := styles.BaseStyle().Render
muted := styles.Muted().Render
headerLines := []string{}
- headerLines = append(headerLines, toMarkdown("# "+m.app.Session.Title, width-4, t.Background()))
+ headerLines = append(headerLines, toMarkdown("# "+m.app.Session.Title, width-6, t.Background()))
if m.app.Session.Share != nil && m.app.Session.Share.Url != "" {
headerLines = append(headerLines, muted(m.app.Session.Share.Url))
} else {
@@ -256,6 +256,7 @@ func (m *messagesComponent) header() string {
header = styles.BaseStyle().
Width(width).
PaddingLeft(2).
+ PaddingRight(2).
// Background(t.BackgroundElement()).
BorderLeft(true).
BorderRight(true).