mirror of
https://github.com/aljazceru/opencode.git
synced 2026-01-07 18:04:54 +01:00
wip: refactoring tui
This commit is contained in:
@@ -10,7 +10,6 @@ import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/sst/opencode/internal/config"
|
||||
"github.com/sst/opencode/internal/fileutil"
|
||||
"github.com/sst/opencode/internal/message"
|
||||
"github.com/sst/opencode/internal/status"
|
||||
"github.com/sst/opencode/internal/tui/state"
|
||||
"github.com/sst/opencode/internal/tui/theme"
|
||||
@@ -24,7 +23,6 @@ type App struct {
|
||||
Session *client.SessionInfo
|
||||
Messages []client.MessageInfo
|
||||
|
||||
MessagesOLD MessageService
|
||||
LogsOLD any // TODO: Define LogService interface when needed
|
||||
HistoryOLD any // TODO: Define HistoryService interface when needed
|
||||
PermissionsOLD any // TODO: Define PermissionService interface when needed
|
||||
@@ -66,14 +64,12 @@ func New(ctx context.Context) (*App, error) {
|
||||
}
|
||||
|
||||
// Create service bridges
|
||||
messageBridge := NewMessageServiceBridge(httpClient)
|
||||
agentBridge := NewAgentServiceBridge(httpClient)
|
||||
|
||||
app := &App{
|
||||
Client: httpClient,
|
||||
Events: eventClient,
|
||||
Session: &client.SessionInfo{},
|
||||
MessagesOLD: messageBridge,
|
||||
PrimaryAgentOLD: agentBridge,
|
||||
Status: status.GetService(),
|
||||
|
||||
@@ -89,8 +85,15 @@ func New(ctx context.Context) (*App, error) {
|
||||
return app, nil
|
||||
}
|
||||
|
||||
type Attachment struct {
|
||||
FilePath string
|
||||
FileName string
|
||||
MimeType string
|
||||
Content []byte
|
||||
}
|
||||
|
||||
// Create creates a new session
|
||||
func (a *App) SendChatMessage(ctx context.Context, text string, attachments []message.Attachment) tea.Cmd {
|
||||
func (a *App) SendChatMessage(ctx context.Context, text string, attachments []Attachment) tea.Cmd {
|
||||
var cmds []tea.Cmd
|
||||
if a.Session.Id == "" {
|
||||
resp, err := a.Client.PostSessionCreateWithResponse(ctx)
|
||||
|
||||
@@ -2,12 +2,8 @@ package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/sst/opencode/internal/message"
|
||||
"github.com/sst/opencode/internal/pubsub"
|
||||
"github.com/sst/opencode/pkg/client"
|
||||
)
|
||||
|
||||
@@ -22,7 +18,7 @@ func NewAgentServiceBridge(client *client.ClientWithResponses) *AgentServiceBrid
|
||||
}
|
||||
|
||||
// Run sends a message to the chat API
|
||||
func (a *AgentServiceBridge) Run(ctx context.Context, sessionID string, text string, attachments ...message.Attachment) (string, error) {
|
||||
func (a *AgentServiceBridge) Run(ctx context.Context, sessionID string, text string, attachments ...Attachment) (string, error) {
|
||||
// TODO: Handle attachments when API supports them
|
||||
if len(attachments) > 0 {
|
||||
// For now, ignore attachments
|
||||
@@ -71,84 +67,3 @@ func (a *AgentServiceBridge) CompactSession(ctx context.Context, sessionID strin
|
||||
// TODO: Not implemented in TypeScript API yet
|
||||
return fmt.Errorf("session compaction not implemented in API")
|
||||
}
|
||||
|
||||
// MessageServiceBridge provides a minimal message service that fetches from the API
|
||||
type MessageServiceBridge struct {
|
||||
client *client.ClientWithResponses
|
||||
broker *pubsub.Broker[message.Message]
|
||||
}
|
||||
|
||||
// NewMessageServiceBridge creates a new message service bridge
|
||||
func NewMessageServiceBridge(client *client.ClientWithResponses) *MessageServiceBridge {
|
||||
return &MessageServiceBridge{
|
||||
client: client,
|
||||
broker: pubsub.NewBroker[message.Message](),
|
||||
}
|
||||
}
|
||||
|
||||
// GetBySession retrieves messages for a session
|
||||
func (m *MessageServiceBridge) GetBySession(ctx context.Context, sessionID string) ([]message.Message, error) {
|
||||
return m.List(ctx, sessionID)
|
||||
}
|
||||
|
||||
// List retrieves messages for a session
|
||||
func (m *MessageServiceBridge) List(ctx context.Context, sessionID string) ([]message.Message, error) {
|
||||
resp, err := m.client.PostSessionMessages(ctx, client.PostSessionMessagesJSONRequestBody{
|
||||
SessionID: sessionID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// The API returns a different format, we'll need to adapt it
|
||||
var rawMessages any
|
||||
if err := json.NewDecoder(resp.Body).Decode(&rawMessages); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: Convert the API message format to our internal format
|
||||
// For now, return empty to avoid compilation errors
|
||||
return []message.Message{}, nil
|
||||
}
|
||||
|
||||
// Create creates a new message - NOT NEEDED, handled by chat API
|
||||
func (m *MessageServiceBridge) Create(ctx context.Context, sessionID string, params message.CreateMessageParams) (message.Message, error) {
|
||||
// Messages are created through the chat API
|
||||
return message.Message{}, fmt.Errorf("use chat API to send messages")
|
||||
}
|
||||
|
||||
// Update updates a message - NOT IMPLEMENTED IN API YET
|
||||
func (m *MessageServiceBridge) Update(ctx context.Context, msg message.Message) (message.Message, error) {
|
||||
// TODO: Not implemented in TypeScript API yet
|
||||
return message.Message{}, fmt.Errorf("message update not implemented in API")
|
||||
}
|
||||
|
||||
// Delete deletes a message - NOT IMPLEMENTED IN API YET
|
||||
func (m *MessageServiceBridge) Delete(ctx context.Context, id string) error {
|
||||
// TODO: Not implemented in TypeScript API yet
|
||||
return fmt.Errorf("message delete not implemented in API")
|
||||
}
|
||||
|
||||
// DeleteSessionMessages deletes all messages for a session - NOT IMPLEMENTED IN API YET
|
||||
func (m *MessageServiceBridge) DeleteSessionMessages(ctx context.Context, sessionID string) error {
|
||||
// TODO: Not implemented in TypeScript API yet
|
||||
return fmt.Errorf("delete session messages not implemented in API")
|
||||
}
|
||||
|
||||
// Get retrieves a message by ID - NOT IMPLEMENTED IN API YET
|
||||
func (m *MessageServiceBridge) Get(ctx context.Context, id string) (message.Message, error) {
|
||||
// TODO: Not implemented in TypeScript API yet
|
||||
return message.Message{}, fmt.Errorf("get message by ID not implemented in API")
|
||||
}
|
||||
|
||||
// ListAfter retrieves messages after a timestamp - NOT IMPLEMENTED IN API YET
|
||||
func (m *MessageServiceBridge) ListAfter(ctx context.Context, sessionID string, timestamp time.Time) ([]message.Message, error) {
|
||||
// TODO: Not implemented in TypeScript API yet
|
||||
return []message.Message{}, fmt.Errorf("list messages after timestamp not implemented in API")
|
||||
}
|
||||
|
||||
// Subscribe subscribes to message events
|
||||
func (m *MessageServiceBridge) Subscribe(ctx context.Context) <-chan pubsub.Event[message.Message] {
|
||||
return m.broker.Subscribe(ctx)
|
||||
}
|
||||
|
||||
@@ -2,29 +2,11 @@ package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/sst/opencode/internal/message"
|
||||
"github.com/sst/opencode/internal/pubsub"
|
||||
)
|
||||
|
||||
// MessageService defines the interface for message operations
|
||||
type MessageService interface {
|
||||
pubsub.Subscriber[message.Message]
|
||||
|
||||
GetBySession(ctx context.Context, sessionID string) ([]message.Message, error)
|
||||
List(ctx context.Context, sessionID string) ([]message.Message, error)
|
||||
Create(ctx context.Context, sessionID string, params message.CreateMessageParams) (message.Message, error)
|
||||
Update(ctx context.Context, msg message.Message) (message.Message, error)
|
||||
Delete(ctx context.Context, id string) error
|
||||
DeleteSessionMessages(ctx context.Context, sessionID string) error
|
||||
Get(ctx context.Context, id string) (message.Message, error)
|
||||
ListAfter(ctx context.Context, sessionID string, timestamp time.Time) ([]message.Message, error)
|
||||
}
|
||||
|
||||
// AgentService defines the interface for agent operations
|
||||
type AgentService interface {
|
||||
Run(ctx context.Context, sessionID string, text string, attachments ...message.Attachment) (string, error)
|
||||
Run(ctx context.Context, sessionID string, text string, attachments ...Attachment) (string, error)
|
||||
Cancel(sessionID string) error
|
||||
IsBusy() bool
|
||||
IsSessionBusy(sessionID string) bool
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
"github.com/sst/opencode/internal/config"
|
||||
"github.com/sst/opencode/internal/message"
|
||||
"github.com/sst/opencode/internal/tui/app"
|
||||
"github.com/sst/opencode/internal/tui/styles"
|
||||
"github.com/sst/opencode/internal/tui/theme"
|
||||
"github.com/sst/opencode/internal/version"
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
type SendMsg struct {
|
||||
Text string
|
||||
Attachments []message.Attachment
|
||||
Attachments []app.Attachment
|
||||
}
|
||||
|
||||
func header(width int) string {
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/charmbracelet/bubbles/textarea"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/sst/opencode/internal/message"
|
||||
"github.com/sst/opencode/internal/status"
|
||||
"github.com/sst/opencode/internal/tui/app"
|
||||
"github.com/sst/opencode/internal/tui/components/dialog"
|
||||
@@ -29,7 +28,7 @@ type editorCmp struct {
|
||||
height int
|
||||
app *app.App
|
||||
textarea textarea.Model
|
||||
attachments []message.Attachment
|
||||
attachments []app.Attachment
|
||||
deleteMode bool
|
||||
history []string
|
||||
historyIndex int
|
||||
@@ -233,7 +232,7 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
if len(imageBytes) != 0 {
|
||||
attachmentName := fmt.Sprintf("clipboard-image-%d", len(m.attachments))
|
||||
attachment := message.Attachment{FilePath: attachmentName, FileName: attachmentName, Content: imageBytes, MimeType: "image/png"}
|
||||
attachment := app.Attachment{FilePath: attachmentName, FileName: attachmentName, Content: imageBytes, MimeType: "image/png"}
|
||||
m.attachments = append(m.attachments, attachment)
|
||||
} else {
|
||||
m.textarea.SetValue(m.textarea.Value() + text)
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
"github.com/sst/opencode/internal/config"
|
||||
"github.com/sst/opencode/internal/message"
|
||||
"github.com/sst/opencode/internal/tui/styles"
|
||||
"github.com/sst/opencode/internal/tui/theme"
|
||||
"github.com/sst/opencode/pkg/client"
|
||||
@@ -244,17 +243,6 @@ func renderAssistantMessage(
|
||||
return strings.Join(messages, "\n\n")
|
||||
}
|
||||
|
||||
func findToolResponse(toolCallID string, futureMessages []message.Message) *message.ToolResult {
|
||||
for _, msg := range futureMessages {
|
||||
for _, result := range msg.ToolResults() {
|
||||
if result.ToolCallID == toolCallID {
|
||||
return &result
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func renderToolName(name string) string {
|
||||
switch name {
|
||||
// case agent.AgentToolName:
|
||||
@@ -354,9 +342,9 @@ func removeWorkingDirPrefix(path string) string {
|
||||
return path
|
||||
}
|
||||
|
||||
func renderToolParams(paramWidth int, toolCall message.ToolCall) string {
|
||||
func renderToolParams(paramWidth int, toolCall any) string {
|
||||
params := ""
|
||||
switch toolCall.Name {
|
||||
switch toolCall {
|
||||
// // case agent.AgentToolName:
|
||||
// // var params agent.AgentParams
|
||||
// // json.Unmarshal([]byte(toolCall.Input), ¶ms)
|
||||
@@ -445,9 +433,9 @@ func renderToolParams(paramWidth int, toolCall message.ToolCall) string {
|
||||
// var params tools.BatchParams
|
||||
// json.Unmarshal([]byte(toolCall.Input), ¶ms)
|
||||
// return renderParams(paramWidth, fmt.Sprintf("%d parallel calls", len(params.Calls)))
|
||||
default:
|
||||
input := strings.ReplaceAll(toolCall.Input, "\n", " ")
|
||||
params = renderParams(paramWidth, input)
|
||||
// default:
|
||||
// input := strings.ReplaceAll(toolCall, "\n", " ")
|
||||
// params = renderParams(paramWidth, input)
|
||||
}
|
||||
return params
|
||||
}
|
||||
@@ -460,21 +448,22 @@ func truncateHeight(content string, height int) string {
|
||||
return content
|
||||
}
|
||||
|
||||
func renderToolResponse(toolCall message.ToolCall, response message.ToolResult, width int) string {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
|
||||
if response.IsError {
|
||||
errContent := fmt.Sprintf("Error: %s", strings.ReplaceAll(response.Content, "\n", " "))
|
||||
errContent = ansi.Truncate(errContent, width-1, "...")
|
||||
return baseStyle.
|
||||
Width(width).
|
||||
Foreground(t.Error()).
|
||||
Render(errContent)
|
||||
}
|
||||
|
||||
resultContent := truncateHeight(response.Content, maxResultHeight)
|
||||
switch toolCall.Name {
|
||||
func renderToolResponse(toolCall any, response any, width int) string {
|
||||
return ""
|
||||
// t := theme.CurrentTheme()
|
||||
// baseStyle := styles.BaseStyle()
|
||||
//
|
||||
// if response.IsError {
|
||||
// errContent := fmt.Sprintf("Error: %s", strings.ReplaceAll(response.Content, "\n", " "))
|
||||
// errContent = ansi.Truncate(errContent, width-1, "...")
|
||||
// return baseStyle.
|
||||
// Width(width).
|
||||
// Foreground(t.Error()).
|
||||
// Render(errContent)
|
||||
// }
|
||||
//
|
||||
// resultContent := truncateHeight(response.Content, maxResultHeight)
|
||||
// switch toolCall.Name {
|
||||
// case agent.AgentToolName:
|
||||
// return styles.ForceReplaceBackgroundWithLipgloss(
|
||||
// toMarkdown(resultContent, false, width),
|
||||
@@ -574,113 +563,113 @@ func renderToolResponse(toolCall message.ToolCall, response message.ToolResult,
|
||||
// }
|
||||
//
|
||||
// return baseStyle.Width(width).Foreground(t.TextMuted()).Render(strings.Join(toolCalls, "\n\n"))
|
||||
default:
|
||||
resultContent = fmt.Sprintf("```text\n%s\n```", resultContent)
|
||||
return styles.ForceReplaceBackgroundWithLipgloss(
|
||||
toMarkdown(resultContent, width),
|
||||
t.Background(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func renderToolMessage(
|
||||
toolCall message.ToolCall,
|
||||
allMessages []message.Message,
|
||||
messagesService message.Service,
|
||||
focusedUIMessageId string,
|
||||
nested bool,
|
||||
width int,
|
||||
position int,
|
||||
) string {
|
||||
if nested {
|
||||
width = width - 3
|
||||
}
|
||||
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
|
||||
style := baseStyle.
|
||||
Width(width - 1).
|
||||
BorderLeft(true).
|
||||
BorderStyle(lipgloss.ThickBorder()).
|
||||
PaddingLeft(1).
|
||||
BorderForeground(t.TextMuted())
|
||||
|
||||
response := findToolResponse(toolCall.ID, allMessages)
|
||||
toolNameText := baseStyle.Foreground(t.TextMuted()).
|
||||
Render(fmt.Sprintf("%s: ", renderToolName(toolCall.Name)))
|
||||
|
||||
if !toolCall.Finished {
|
||||
// Get a brief description of what the tool is doing
|
||||
toolAction := renderToolAction(toolCall.Name)
|
||||
|
||||
progressText := baseStyle.
|
||||
Width(width - 2 - lipgloss.Width(toolNameText)).
|
||||
Foreground(t.TextMuted()).
|
||||
Render(fmt.Sprintf("%s", toolAction))
|
||||
|
||||
content := style.Render(lipgloss.JoinHorizontal(lipgloss.Left, toolNameText, progressText))
|
||||
return content
|
||||
}
|
||||
|
||||
params := renderToolParams(width-1-lipgloss.Width(toolNameText), toolCall)
|
||||
responseContent := ""
|
||||
if response != nil {
|
||||
responseContent = renderToolResponse(toolCall, *response, width-2)
|
||||
responseContent = strings.TrimSuffix(responseContent, "\n")
|
||||
} else {
|
||||
responseContent = baseStyle.
|
||||
Italic(true).
|
||||
Width(width - 2).
|
||||
Foreground(t.TextMuted()).
|
||||
Render("Waiting for response...")
|
||||
}
|
||||
|
||||
parts := []string{}
|
||||
if !nested {
|
||||
formattedParams := baseStyle.
|
||||
Width(width - 2 - lipgloss.Width(toolNameText)).
|
||||
Foreground(t.TextMuted()).
|
||||
Render(params)
|
||||
|
||||
parts = append(parts, lipgloss.JoinHorizontal(lipgloss.Left, toolNameText, formattedParams))
|
||||
} else {
|
||||
prefix := baseStyle.
|
||||
Foreground(t.TextMuted()).
|
||||
Render(" └ ")
|
||||
formattedParams := baseStyle.
|
||||
Width(width - 2 - lipgloss.Width(toolNameText)).
|
||||
Foreground(t.TextMuted()).
|
||||
Render(params)
|
||||
parts = append(parts, lipgloss.JoinHorizontal(lipgloss.Left, prefix, toolNameText, formattedParams))
|
||||
}
|
||||
|
||||
// if toolCall.Name == agent.AgentToolName {
|
||||
// taskMessages, _ := messagesService.List(context.Background(), toolCall.ID)
|
||||
// toolCalls := []message.ToolCall{}
|
||||
// for _, v := range taskMessages {
|
||||
// toolCalls = append(toolCalls, v.ToolCalls()...)
|
||||
// }
|
||||
// for _, call := range toolCalls {
|
||||
// rendered := renderToolMessage(call, []message.Message{}, messagesService, focusedUIMessageId, true, width, 0)
|
||||
// parts = append(parts, rendered.content)
|
||||
// }
|
||||
// default:
|
||||
// resultContent = fmt.Sprintf("```text\n%s\n```", resultContent)
|
||||
// return styles.ForceReplaceBackgroundWithLipgloss(
|
||||
// toMarkdown(resultContent, width),
|
||||
// t.Background(),
|
||||
// )
|
||||
// }
|
||||
if responseContent != "" && !nested {
|
||||
parts = append(parts, responseContent)
|
||||
}
|
||||
|
||||
content := style.Render(
|
||||
lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
parts...,
|
||||
),
|
||||
)
|
||||
if nested {
|
||||
content = lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
parts...,
|
||||
)
|
||||
}
|
||||
return content
|
||||
}
|
||||
|
||||
// func renderToolMessage(
|
||||
// toolCall message.ToolCall,
|
||||
// allMessages []message.Message,
|
||||
// messagesService message.Service,
|
||||
// focusedUIMessageId string,
|
||||
// nested bool,
|
||||
// width int,
|
||||
// position int,
|
||||
// ) string {
|
||||
// if nested {
|
||||
// width = width - 3
|
||||
// }
|
||||
//
|
||||
// t := theme.CurrentTheme()
|
||||
// baseStyle := styles.BaseStyle()
|
||||
//
|
||||
// style := baseStyle.
|
||||
// Width(width - 1).
|
||||
// BorderLeft(true).
|
||||
// BorderStyle(lipgloss.ThickBorder()).
|
||||
// PaddingLeft(1).
|
||||
// BorderForeground(t.TextMuted())
|
||||
//
|
||||
// response := findToolResponse(toolCall.ID, allMessages)
|
||||
// toolNameText := baseStyle.Foreground(t.TextMuted()).
|
||||
// Render(fmt.Sprintf("%s: ", renderToolName(toolCall.Name)))
|
||||
//
|
||||
// if !toolCall.Finished {
|
||||
// // Get a brief description of what the tool is doing
|
||||
// toolAction := renderToolAction(toolCall.Name)
|
||||
//
|
||||
// progressText := baseStyle.
|
||||
// Width(width - 2 - lipgloss.Width(toolNameText)).
|
||||
// Foreground(t.TextMuted()).
|
||||
// Render(fmt.Sprintf("%s", toolAction))
|
||||
//
|
||||
// content := style.Render(lipgloss.JoinHorizontal(lipgloss.Left, toolNameText, progressText))
|
||||
// return content
|
||||
// }
|
||||
//
|
||||
// params := renderToolParams(width-1-lipgloss.Width(toolNameText), toolCall)
|
||||
// responseContent := ""
|
||||
// if response != nil {
|
||||
// responseContent = renderToolResponse(toolCall, *response, width-2)
|
||||
// responseContent = strings.TrimSuffix(responseContent, "\n")
|
||||
// } else {
|
||||
// responseContent = baseStyle.
|
||||
// Italic(true).
|
||||
// Width(width - 2).
|
||||
// Foreground(t.TextMuted()).
|
||||
// Render("Waiting for response...")
|
||||
// }
|
||||
//
|
||||
// parts := []string{}
|
||||
// if !nested {
|
||||
// formattedParams := baseStyle.
|
||||
// Width(width - 2 - lipgloss.Width(toolNameText)).
|
||||
// Foreground(t.TextMuted()).
|
||||
// Render(params)
|
||||
//
|
||||
// parts = append(parts, lipgloss.JoinHorizontal(lipgloss.Left, toolNameText, formattedParams))
|
||||
// } else {
|
||||
// prefix := baseStyle.
|
||||
// Foreground(t.TextMuted()).
|
||||
// Render(" └ ")
|
||||
// formattedParams := baseStyle.
|
||||
// Width(width - 2 - lipgloss.Width(toolNameText)).
|
||||
// Foreground(t.TextMuted()).
|
||||
// Render(params)
|
||||
// parts = append(parts, lipgloss.JoinHorizontal(lipgloss.Left, prefix, toolNameText, formattedParams))
|
||||
// }
|
||||
//
|
||||
// // if toolCall.Name == agent.AgentToolName {
|
||||
// // taskMessages, _ := messagesService.List(context.Background(), toolCall.ID)
|
||||
// // toolCalls := []message.ToolCall{}
|
||||
// // for _, v := range taskMessages {
|
||||
// // toolCalls = append(toolCalls, v.ToolCalls()...)
|
||||
// // }
|
||||
// // for _, call := range toolCalls {
|
||||
// // rendered := renderToolMessage(call, []message.Message{}, messagesService, focusedUIMessageId, true, width, 0)
|
||||
// // parts = append(parts, rendered.content)
|
||||
// // }
|
||||
// // }
|
||||
// if responseContent != "" && !nested {
|
||||
// parts = append(parts, responseContent)
|
||||
// }
|
||||
//
|
||||
// content := style.Render(
|
||||
// lipgloss.JoinVertical(
|
||||
// lipgloss.Left,
|
||||
// parts...,
|
||||
// ),
|
||||
// )
|
||||
// if nested {
|
||||
// content = lipgloss.JoinVertical(
|
||||
// lipgloss.Left,
|
||||
// parts...,
|
||||
// )
|
||||
// }
|
||||
// return content
|
||||
// }
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/charmbracelet/bubbles/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/sst/opencode/internal/message"
|
||||
"github.com/sst/opencode/internal/tui/app"
|
||||
"github.com/sst/opencode/internal/tui/components/dialog"
|
||||
"github.com/sst/opencode/internal/tui/state"
|
||||
@@ -177,41 +176,41 @@ func (m *messagesCmp) View() string {
|
||||
)
|
||||
}
|
||||
|
||||
func hasToolsWithoutResponse(messages []message.Message) bool {
|
||||
toolCalls := make([]message.ToolCall, 0)
|
||||
toolResults := make([]message.ToolResult, 0)
|
||||
for _, m := range messages {
|
||||
toolCalls = append(toolCalls, m.ToolCalls()...)
|
||||
toolResults = append(toolResults, m.ToolResults()...)
|
||||
}
|
||||
// func hasToolsWithoutResponse(messages []message.Message) bool {
|
||||
// toolCalls := make([]message.ToolCall, 0)
|
||||
// toolResults := make([]message.ToolResult, 0)
|
||||
// for _, m := range messages {
|
||||
// toolCalls = append(toolCalls, m.ToolCalls()...)
|
||||
// toolResults = append(toolResults, m.ToolResults()...)
|
||||
// }
|
||||
//
|
||||
// for _, v := range toolCalls {
|
||||
// found := false
|
||||
// for _, r := range toolResults {
|
||||
// if v.ID == r.ToolCallID {
|
||||
// found = true
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// if !found && v.Finished {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
|
||||
for _, v := range toolCalls {
|
||||
found := false
|
||||
for _, r := range toolResults {
|
||||
if v.ID == r.ToolCallID {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found && v.Finished {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasUnfinishedToolCalls(messages []message.Message) bool {
|
||||
toolCalls := make([]message.ToolCall, 0)
|
||||
for _, m := range messages {
|
||||
toolCalls = append(toolCalls, m.ToolCalls()...)
|
||||
}
|
||||
for _, v := range toolCalls {
|
||||
if !v.Finished {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
// func hasUnfinishedToolCalls(messages []message.Message) bool {
|
||||
// toolCalls := make([]message.ToolCall, 0)
|
||||
// for _, m := range messages {
|
||||
// toolCalls = append(toolCalls, m.ToolCalls()...)
|
||||
// }
|
||||
// for _, v := range toolCalls {
|
||||
// if !v.Finished {
|
||||
// return true
|
||||
// }
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
|
||||
func (m *messagesCmp) working() string {
|
||||
text := ""
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
"github.com/charmbracelet/bubbles/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/sst/opencode/internal/message"
|
||||
"github.com/sst/opencode/internal/status"
|
||||
"github.com/sst/opencode/internal/tui/app"
|
||||
"github.com/sst/opencode/internal/tui/image"
|
||||
@@ -116,7 +115,7 @@ func (s stack) Pop() (stack, int) {
|
||||
}
|
||||
|
||||
type AttachmentAddedMsg struct {
|
||||
Attachment message.Attachment
|
||||
Attachment app.Attachment
|
||||
}
|
||||
|
||||
func (f *filepickerCmp) Init() tea.Cmd {
|
||||
@@ -269,7 +268,7 @@ func (f *filepickerCmp) addAttachmentToMessage() (tea.Model, tea.Cmd) {
|
||||
mimeBufferSize := min(512, len(content))
|
||||
mimeType := http.DetectContentType(content[:mimeBufferSize])
|
||||
fileName := filepath.Base(selectedFilePath)
|
||||
attachment := message.Attachment{FilePath: selectedFilePath, FileName: fileName, MimeType: mimeType, Content: content}
|
||||
attachment := app.Attachment{FilePath: selectedFilePath, FileName: fileName, MimeType: mimeType, Content: content}
|
||||
f.selectedFile = ""
|
||||
return f, util.CmdHandler(AttachmentAddedMsg{attachment})
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/sst/opencode/internal/completions"
|
||||
"github.com/sst/opencode/internal/message"
|
||||
"github.com/sst/opencode/internal/status"
|
||||
"github.com/sst/opencode/internal/tui/app"
|
||||
"github.com/sst/opencode/internal/tui/components/chat"
|
||||
@@ -161,7 +160,7 @@ func (p *chatPage) clearSidebar() tea.Cmd {
|
||||
return p.layout.ClearRightPanel()
|
||||
}
|
||||
|
||||
func (p *chatPage) sendMessage(text string, attachments []message.Attachment) tea.Cmd {
|
||||
func (p *chatPage) sendMessage(text string, attachments []app.Attachment) tea.Cmd {
|
||||
var cmds []tea.Cmd
|
||||
cmd := p.app.SendChatMessage(context.Background(), text, attachments)
|
||||
cmds = append(cmds, cmd)
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
"github.com/sst/opencode/internal/config"
|
||||
"github.com/sst/opencode/internal/tui/app"
|
||||
|
||||
"github.com/sst/opencode/internal/message"
|
||||
"github.com/sst/opencode/internal/permission"
|
||||
"github.com/sst/opencode/internal/pubsub"
|
||||
"github.com/sst/opencode/internal/status"
|
||||
@@ -574,11 +573,6 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
case pubsub.Event[message.Message]:
|
||||
a.pages[page.ChatPage], cmd = a.pages[page.ChatPage].Update(msg)
|
||||
cmds = append(cmds, cmd)
|
||||
return a, tea.Batch(cmds...)
|
||||
|
||||
default:
|
||||
f, filepickerCmd := a.filepicker.Update(msg)
|
||||
a.filepicker = f.(dialog.FilepickerCmp)
|
||||
|
||||
Reference in New Issue
Block a user