wip: refactoring tui
This commit is contained in:
@@ -3,7 +3,6 @@ package app
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -12,7 +11,6 @@ import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/sst/opencode/internal/config"
|
||||
"github.com/sst/opencode/internal/fileutil"
|
||||
"github.com/sst/opencode/internal/lsp"
|
||||
"github.com/sst/opencode/internal/message"
|
||||
"github.com/sst/opencode/internal/session"
|
||||
"github.com/sst/opencode/internal/status"
|
||||
@@ -23,23 +21,21 @@ import (
|
||||
)
|
||||
|
||||
type App struct {
|
||||
State map[string]any
|
||||
Client *client.ClientWithResponses
|
||||
Events *client.Client
|
||||
State map[string]any
|
||||
Session *client.SessionInfo
|
||||
Messages []client.SessionMessage
|
||||
|
||||
CurrentSession *session.Session
|
||||
Logs any // TODO: Define LogService interface when needed
|
||||
Sessions SessionService
|
||||
Messages MessageService
|
||||
History any // TODO: Define HistoryService interface when needed
|
||||
Permissions any // TODO: Define PermissionService interface when needed
|
||||
Status status.Service
|
||||
Client *client.ClientWithResponses
|
||||
Events *client.Client
|
||||
CurrentSessionOLD *session.Session
|
||||
SessionsOLD SessionService
|
||||
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
|
||||
Status status.Service
|
||||
|
||||
PrimaryAgent AgentService
|
||||
|
||||
LSPClients map[string]*lsp.Client
|
||||
|
||||
clientsMutex sync.RWMutex
|
||||
PrimaryAgentOLD AgentService
|
||||
|
||||
watcherCancelFuncs []context.CancelFunc
|
||||
cancelFuncsMutex sync.Mutex
|
||||
@@ -80,20 +76,20 @@ func New(ctx context.Context) (*App, error) {
|
||||
agentBridge := NewAgentServiceBridge(httpClient)
|
||||
|
||||
app := &App{
|
||||
State: make(map[string]any),
|
||||
Client: httpClient,
|
||||
Events: eventClient,
|
||||
CurrentSession: &session.Session{},
|
||||
Sessions: sessionBridge,
|
||||
Messages: messageBridge,
|
||||
PrimaryAgent: agentBridge,
|
||||
Status: status.GetService(),
|
||||
LSPClients: make(map[string]*lsp.Client),
|
||||
State: make(map[string]any),
|
||||
Client: httpClient,
|
||||
Events: eventClient,
|
||||
Session: &client.SessionInfo{},
|
||||
CurrentSessionOLD: &session.Session{},
|
||||
SessionsOLD: sessionBridge,
|
||||
MessagesOLD: messageBridge,
|
||||
PrimaryAgentOLD: agentBridge,
|
||||
Status: status.GetService(),
|
||||
|
||||
// TODO: These services need API endpoints:
|
||||
Logs: nil, // logging.GetService(),
|
||||
History: nil, // history.GetService(),
|
||||
Permissions: nil, // permission.GetService(),
|
||||
LogsOLD: nil, // logging.GetService(),
|
||||
HistoryOLD: nil, // history.GetService(),
|
||||
PermissionsOLD: nil, // permission.GetService(),
|
||||
}
|
||||
|
||||
// Initialize theme based on configuration
|
||||
@@ -105,30 +101,28 @@ func New(ctx context.Context) (*App, error) {
|
||||
// Create creates a new session
|
||||
func (a *App) SendChatMessage(ctx context.Context, text string, attachments []message.Attachment) tea.Cmd {
|
||||
var cmds []tea.Cmd
|
||||
if a.CurrentSession.ID == "" {
|
||||
if a.Session.Id == "" {
|
||||
resp, err := a.Client.PostSessionCreateWithResponse(ctx)
|
||||
if err != nil {
|
||||
// return session.Session{}, err
|
||||
status.Error(err.Error())
|
||||
return nil
|
||||
}
|
||||
if resp.StatusCode() != 200 {
|
||||
// return session.Session{}, fmt.Errorf("failed to create session: %d", resp.StatusCode())
|
||||
status.Error(fmt.Sprintf("failed to create session: %d", resp.StatusCode()))
|
||||
return nil
|
||||
}
|
||||
info := resp.JSON200
|
||||
|
||||
// Convert to old session type
|
||||
info := resp.JSON200
|
||||
a.Session = info
|
||||
|
||||
// Convert to old session type for backwards compatibility
|
||||
newSession := session.Session{
|
||||
ID: info.Id,
|
||||
Title: info.Title,
|
||||
CreatedAt: time.Now(), // API doesn't provide this yet
|
||||
UpdatedAt: time.Now(), // API doesn't provide this yet
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
status.Error(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
a.CurrentSession = &newSession
|
||||
a.CurrentSessionOLD = &newSession
|
||||
|
||||
cmds = append(cmds, util.CmdHandler(state.SessionSelectedMsg(&newSession)))
|
||||
}
|
||||
@@ -147,7 +141,7 @@ func (a *App) SendChatMessage(ctx context.Context, text string, attachments []me
|
||||
parts := []client.SessionMessagePart{part}
|
||||
|
||||
go a.Client.PostSessionChatWithResponse(ctx, client.PostSessionChatJSONRequestBody{
|
||||
SessionID: a.CurrentSession.ID,
|
||||
SessionID: a.Session.Id,
|
||||
Parts: parts,
|
||||
ProviderID: "anthropic",
|
||||
ModelID: "claude-sonnet-4-20250514",
|
||||
@@ -234,18 +228,4 @@ func (app *App) Shutdown() {
|
||||
}
|
||||
app.cancelFuncsMutex.Unlock()
|
||||
app.watcherWG.Wait()
|
||||
|
||||
// Perform additional cleanup for LSP clients
|
||||
app.clientsMutex.RLock()
|
||||
clients := make(map[string]*lsp.Client, len(app.LSPClients))
|
||||
maps.Copy(clients, app.LSPClients)
|
||||
app.clientsMutex.RUnlock()
|
||||
|
||||
for name, client := range clients {
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
if err := client.Shutdown(shutdownCtx); err != nil {
|
||||
slog.Error("Failed to shutdown LSP client", "name", name, "error", err)
|
||||
}
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ func (m *editorCmp) Init() tea.Cmd {
|
||||
}
|
||||
|
||||
func (m *editorCmp) send() tea.Cmd {
|
||||
if m.app.PrimaryAgent.IsSessionBusy(m.app.CurrentSession.ID) {
|
||||
if m.app.PrimaryAgentOLD.IsSessionBusy(m.app.CurrentSessionOLD.ID) {
|
||||
status.Warn("Agent is working, please wait...")
|
||||
return nil
|
||||
}
|
||||
@@ -217,7 +217,7 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, nil
|
||||
}
|
||||
if key.Matches(msg, editorMaps.OpenEditor) {
|
||||
if m.app.PrimaryAgent.IsSessionBusy(m.app.CurrentSession.ID) {
|
||||
if m.app.PrimaryAgentOLD.IsSessionBusy(m.app.CurrentSessionOLD.ID) {
|
||||
status.Warn("Agent is working, please wait...")
|
||||
return m, nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
@@ -12,12 +11,12 @@ import (
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
"github.com/sst/opencode/internal/config"
|
||||
"github.com/sst/opencode/internal/diff"
|
||||
"github.com/sst/opencode/internal/llm/agent"
|
||||
"github.com/sst/opencode/internal/llm/models"
|
||||
"github.com/sst/opencode/internal/llm/tools"
|
||||
"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"
|
||||
)
|
||||
|
||||
type uiMessageType int
|
||||
@@ -33,8 +32,6 @@ const (
|
||||
type uiMessage struct {
|
||||
ID string
|
||||
messageType uiMessageType
|
||||
position int
|
||||
height int
|
||||
content string
|
||||
}
|
||||
|
||||
@@ -48,7 +45,7 @@ func renderMessage(msg string, isUser bool, isFocused bool, width int, info ...s
|
||||
t := theme.CurrentTheme()
|
||||
|
||||
style := styles.BaseStyle().
|
||||
Width(width - 1).
|
||||
// Width(width - 1).
|
||||
BorderLeft(true).
|
||||
Foreground(t.TextMuted()).
|
||||
BorderForeground(t.Primary()).
|
||||
@@ -79,28 +76,29 @@ func renderMessage(msg string, isUser bool, isFocused bool, width int, info ...s
|
||||
return rendered
|
||||
}
|
||||
|
||||
func renderUserMessage(msg message.Message, isFocused bool, width int, position int) uiMessage {
|
||||
var styledAttachments []string
|
||||
func renderUserMessage(msg client.SessionMessage, isFocused bool, width int, position int) uiMessage {
|
||||
// var styledAttachments []string
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
attachmentStyles := baseStyle.
|
||||
MarginLeft(1).
|
||||
Background(t.TextMuted()).
|
||||
Foreground(t.Text())
|
||||
for _, attachment := range msg.BinaryContent() {
|
||||
file := filepath.Base(attachment.Path)
|
||||
var filename string
|
||||
if len(file) > 10 {
|
||||
filename = fmt.Sprintf(" %s %s...", styles.DocumentIcon, file[0:7])
|
||||
} else {
|
||||
filename = fmt.Sprintf(" %s %s", styles.DocumentIcon, file)
|
||||
}
|
||||
styledAttachments = append(styledAttachments, attachmentStyles.Render(filename))
|
||||
}
|
||||
// attachmentStyles := baseStyle.
|
||||
// MarginLeft(1).
|
||||
// Background(t.TextMuted()).
|
||||
// Foreground(t.Text())
|
||||
// for _, attachment := range msg.BinaryContent() {
|
||||
// file := filepath.Base(attachment.Path)
|
||||
// var filename string
|
||||
// if len(file) > 10 {
|
||||
// filename = fmt.Sprintf(" %s %s...", styles.DocumentIcon, file[0:7])
|
||||
// } else {
|
||||
// filename = fmt.Sprintf(" %s %s", styles.DocumentIcon, file)
|
||||
// }
|
||||
// styledAttachments = append(styledAttachments, attachmentStyles.Render(filename))
|
||||
// }
|
||||
|
||||
info := []string{}
|
||||
|
||||
// Add timestamp info
|
||||
info := []string{}
|
||||
timestamp := msg.CreatedAt.Local().Format("02 Jan 2006 03:04 PM")
|
||||
timestamp := time.UnixMilli(int64(msg.Metadata.Time.Created)).Local().Format("02 Jan 2006 03:04 PM")
|
||||
username, _ := config.GetUsername()
|
||||
info = append(info, baseStyle.
|
||||
Width(width-1).
|
||||
@@ -109,17 +107,27 @@ func renderUserMessage(msg message.Message, isFocused bool, width int, position
|
||||
)
|
||||
|
||||
content := ""
|
||||
if len(styledAttachments) > 0 {
|
||||
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, info...)
|
||||
// if len(styledAttachments) > 0 {
|
||||
// attachmentContent := baseStyle.Width(width).Render(lipgloss.JoinHorizontal(lipgloss.Left, styledAttachments...))
|
||||
// content = renderMessage(msg.Content().String(), true, isFocused, width, append(info, attachmentContent)...)
|
||||
// } else {
|
||||
for _, p := range msg.Parts {
|
||||
part, err := p.ValueByDiscriminator()
|
||||
if err != nil {
|
||||
continue //TODO: handle error?
|
||||
}
|
||||
|
||||
switch part.(type) {
|
||||
case client.SessionMessagePartText:
|
||||
textPart := part.(client.SessionMessagePartText)
|
||||
content = renderMessage(textPart.Text, true, isFocused, width, info...)
|
||||
}
|
||||
}
|
||||
// content = renderMessage(msg.Parts, true, isFocused, width, info...)
|
||||
|
||||
userMsg := uiMessage{
|
||||
ID: msg.ID,
|
||||
ID: msg.Id,
|
||||
messageType: userMessageType,
|
||||
position: position,
|
||||
height: lipgloss.Height(content),
|
||||
content: content,
|
||||
}
|
||||
return userMsg
|
||||
@@ -193,11 +201,11 @@ func renderAssistantMessage(
|
||||
messages = append(messages, uiMessage{
|
||||
ID: msg.ID,
|
||||
messageType: assistantMessageType,
|
||||
position: position,
|
||||
height: lipgloss.Height(content),
|
||||
content: content,
|
||||
// position: position,
|
||||
// height: lipgloss.Height(content),
|
||||
content: content,
|
||||
})
|
||||
position += messages[0].height
|
||||
// position += messages[0].height
|
||||
position++ // for the space
|
||||
} else if thinking && thinkingContent != "" {
|
||||
// Render the thinking content with timestamp
|
||||
@@ -205,9 +213,9 @@ func renderAssistantMessage(
|
||||
messages = append(messages, uiMessage{
|
||||
ID: msg.ID,
|
||||
messageType: assistantMessageType,
|
||||
position: position,
|
||||
height: lipgloss.Height(content),
|
||||
content: content,
|
||||
// position: position,
|
||||
// height: lipgloss.Height(content),
|
||||
content: content,
|
||||
})
|
||||
position += lipgloss.Height(content)
|
||||
position++ // for the space
|
||||
@@ -226,7 +234,7 @@ func renderAssistantMessage(
|
||||
i+1,
|
||||
)
|
||||
messages = append(messages, toolCallContent)
|
||||
position += toolCallContent.height
|
||||
// position += toolCallContent.height
|
||||
position++ // for the space
|
||||
}
|
||||
}
|
||||
@@ -246,8 +254,8 @@ func findToolResponse(toolCallID string, futureMessages []message.Message) *mess
|
||||
|
||||
func toolName(name string) string {
|
||||
switch name {
|
||||
case agent.AgentToolName:
|
||||
return "Task"
|
||||
// case agent.AgentToolName:
|
||||
// return "Task"
|
||||
case tools.BashToolName:
|
||||
return "Bash"
|
||||
case tools.EditToolName:
|
||||
@@ -274,8 +282,8 @@ func toolName(name string) string {
|
||||
|
||||
func getToolAction(name string) string {
|
||||
switch name {
|
||||
case agent.AgentToolName:
|
||||
return "Preparing prompt..."
|
||||
// case agent.AgentToolName:
|
||||
// return "Preparing prompt..."
|
||||
case tools.BashToolName:
|
||||
return "Building command..."
|
||||
case tools.EditToolName:
|
||||
@@ -363,11 +371,11 @@ func removeWorkingDirPrefix(path string) string {
|
||||
func renderToolParams(paramWidth int, toolCall message.ToolCall) string {
|
||||
params := ""
|
||||
switch toolCall.Name {
|
||||
case agent.AgentToolName:
|
||||
var params agent.AgentParams
|
||||
json.Unmarshal([]byte(toolCall.Input), ¶ms)
|
||||
prompt := strings.ReplaceAll(params.Prompt, "\n", " ")
|
||||
return renderParams(paramWidth, prompt)
|
||||
// case agent.AgentToolName:
|
||||
// var params agent.AgentParams
|
||||
// json.Unmarshal([]byte(toolCall.Input), ¶ms)
|
||||
// prompt := strings.ReplaceAll(params.Prompt, "\n", " ")
|
||||
// return renderParams(paramWidth, prompt)
|
||||
case tools.BashToolName:
|
||||
var params tools.BashParams
|
||||
json.Unmarshal([]byte(toolCall.Input), ¶ms)
|
||||
@@ -481,11 +489,11 @@ func renderToolResponse(toolCall message.ToolCall, response message.ToolResult,
|
||||
|
||||
resultContent := truncateHeight(response.Content, maxResultHeight)
|
||||
switch toolCall.Name {
|
||||
case agent.AgentToolName:
|
||||
return styles.ForceReplaceBackgroundWithLipgloss(
|
||||
toMarkdown(resultContent, false, width),
|
||||
t.Background(),
|
||||
)
|
||||
// case agent.AgentToolName:
|
||||
// return styles.ForceReplaceBackgroundWithLipgloss(
|
||||
// toMarkdown(resultContent, false, width),
|
||||
// t.Background(),
|
||||
// )
|
||||
case tools.BashToolName:
|
||||
resultContent = fmt.Sprintf("```bash\n%s\n```", resultContent)
|
||||
return styles.ForceReplaceBackgroundWithLipgloss(
|
||||
@@ -628,9 +636,9 @@ func renderToolMessage(
|
||||
content := style.Render(lipgloss.JoinHorizontal(lipgloss.Left, toolNameText, progressText))
|
||||
toolMsg := uiMessage{
|
||||
messageType: toolMessageType,
|
||||
position: position,
|
||||
height: lipgloss.Height(content),
|
||||
content: content,
|
||||
// position: position,
|
||||
// height: lipgloss.Height(content),
|
||||
content: content,
|
||||
}
|
||||
return toolMsg
|
||||
}
|
||||
@@ -667,17 +675,17 @@ func renderToolMessage(
|
||||
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 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)
|
||||
}
|
||||
@@ -696,9 +704,9 @@ func renderToolMessage(
|
||||
}
|
||||
toolMsg := uiMessage{
|
||||
messageType: toolMessageType,
|
||||
position: position,
|
||||
height: lipgloss.Height(content),
|
||||
content: content,
|
||||
// position: position,
|
||||
// height: lipgloss.Height(content),
|
||||
content: content,
|
||||
}
|
||||
return toolMsg
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"time"
|
||||
@@ -13,14 +11,13 @@ import (
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/sst/opencode/internal/message"
|
||||
"github.com/sst/opencode/internal/pubsub"
|
||||
"github.com/sst/opencode/internal/session"
|
||||
"github.com/sst/opencode/internal/status"
|
||||
"github.com/sst/opencode/internal/tui/app"
|
||||
"github.com/sst/opencode/internal/tui/components/dialog"
|
||||
"github.com/sst/opencode/internal/tui/state"
|
||||
"github.com/sst/opencode/internal/tui/styles"
|
||||
"github.com/sst/opencode/internal/tui/theme"
|
||||
"github.com/sst/opencode/pkg/client"
|
||||
)
|
||||
|
||||
type cacheItem struct {
|
||||
@@ -32,7 +29,6 @@ type messagesCmp struct {
|
||||
app *app.App
|
||||
width, height int
|
||||
viewport viewport.Model
|
||||
messages []message.Message
|
||||
uiMessages []uiMessage
|
||||
currentMsgID string
|
||||
cachedContent map[string]cacheItem
|
||||
@@ -75,6 +71,8 @@ func (m *messagesCmp) Init() tea.Cmd {
|
||||
}
|
||||
|
||||
func (m *messagesCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.renderView()
|
||||
|
||||
var cmds []tea.Cmd
|
||||
switch msg := msg.(type) {
|
||||
case dialog.ThemeChangedMsg:
|
||||
@@ -90,7 +88,7 @@ func (m *messagesCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
cmd := m.Reload(msg)
|
||||
return m, cmd
|
||||
case state.SessionClearedMsg:
|
||||
m.messages = make([]message.Message, 0)
|
||||
// m.messages = make([]message.Message, 0)
|
||||
m.currentMsgID = ""
|
||||
m.rendering = false
|
||||
return m, nil
|
||||
@@ -104,62 +102,63 @@ func (m *messagesCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
case renderFinishedMsg:
|
||||
m.rendering = false
|
||||
m.viewport.GotoBottom()
|
||||
|
||||
case state.StateUpdatedMsg:
|
||||
m.renderView()
|
||||
m.viewport.GotoBottom()
|
||||
|
||||
case pubsub.Event[message.Message]:
|
||||
needsRerender := false
|
||||
if msg.Type == message.EventMessageCreated {
|
||||
if msg.Payload.SessionID == m.app.CurrentSession.ID {
|
||||
messageExists := false
|
||||
for _, v := range m.messages {
|
||||
if v.ID == msg.Payload.ID {
|
||||
messageExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !messageExists {
|
||||
if len(m.messages) > 0 {
|
||||
lastMsgID := m.messages[len(m.messages)-1].ID
|
||||
delete(m.cachedContent, lastMsgID)
|
||||
}
|
||||
|
||||
m.messages = append(m.messages, msg.Payload)
|
||||
delete(m.cachedContent, m.currentMsgID)
|
||||
m.currentMsgID = msg.Payload.ID
|
||||
needsRerender = true
|
||||
}
|
||||
}
|
||||
// There are tool calls from the child task
|
||||
for _, v := range m.messages {
|
||||
for _, c := range v.ToolCalls() {
|
||||
if c.ID == msg.Payload.SessionID {
|
||||
delete(m.cachedContent, v.ID)
|
||||
needsRerender = true
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if msg.Type == message.EventMessageUpdated && msg.Payload.SessionID == m.app.CurrentSession.ID {
|
||||
for i, v := range m.messages {
|
||||
if v.ID == msg.Payload.ID {
|
||||
m.messages[i] = msg.Payload
|
||||
delete(m.cachedContent, msg.Payload.ID)
|
||||
needsRerender = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if needsRerender {
|
||||
m.renderView()
|
||||
if len(m.messages) > 0 {
|
||||
if (msg.Type == message.EventMessageCreated) ||
|
||||
(msg.Type == message.EventMessageUpdated && msg.Payload.ID == m.messages[len(m.messages)-1].ID) {
|
||||
m.viewport.GotoBottom()
|
||||
}
|
||||
}
|
||||
}
|
||||
// case pubsub.Event[message.Message]:
|
||||
// needsRerender := false
|
||||
// if msg.Type == message.EventMessageCreated {
|
||||
// if msg.Payload.SessionID == m.app.CurrentSessionOLD.ID {
|
||||
// messageExists := false
|
||||
// for _, v := range m.messages {
|
||||
// if v.ID == msg.Payload.ID {
|
||||
// messageExists = true
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// if !messageExists {
|
||||
// if len(m.messages) > 0 {
|
||||
// lastMsgID := m.messages[len(m.messages)-1].ID
|
||||
// delete(m.cachedContent, lastMsgID)
|
||||
// }
|
||||
//
|
||||
// m.messages = append(m.messages, msg.Payload)
|
||||
// delete(m.cachedContent, m.currentMsgID)
|
||||
// m.currentMsgID = msg.Payload.ID
|
||||
// needsRerender = true
|
||||
// }
|
||||
// }
|
||||
// // There are tool calls from the child task
|
||||
// for _, v := range m.messages {
|
||||
// for _, c := range v.ToolCalls() {
|
||||
// if c.ID == msg.Payload.SessionID {
|
||||
// delete(m.cachedContent, v.ID)
|
||||
// needsRerender = true
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } else if msg.Type == message.EventMessageUpdated && msg.Payload.SessionID == m.app.CurrentSessionOLD.ID {
|
||||
// for i, v := range m.messages {
|
||||
// if v.ID == msg.Payload.ID {
|
||||
// m.messages[i] = msg.Payload
|
||||
// delete(m.cachedContent, msg.Payload.ID)
|
||||
// needsRerender = true
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if needsRerender {
|
||||
// m.renderView()
|
||||
// if len(m.messages) > 0 {
|
||||
// if (msg.Type == message.EventMessageCreated) ||
|
||||
// (msg.Type == message.EventMessageUpdated && msg.Payload.ID == m.messages[len(m.messages)-1].ID) {
|
||||
// m.viewport.GotoBottom()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
spinner, cmd := m.spinner.Update(msg)
|
||||
@@ -169,7 +168,7 @@ func (m *messagesCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
func (m *messagesCmp) IsAgentWorking() bool {
|
||||
return m.app.PrimaryAgent.IsSessionBusy(m.app.CurrentSession.ID)
|
||||
return m.app.PrimaryAgentOLD.IsSessionBusy(m.app.CurrentSessionOLD.ID)
|
||||
}
|
||||
|
||||
func formatTimeDifference(unixTime1, unixTime2 int64) string {
|
||||
@@ -192,48 +191,48 @@ func (m *messagesCmp) renderView() {
|
||||
if m.width == 0 {
|
||||
return
|
||||
}
|
||||
for inx, msg := range m.messages {
|
||||
for _, msg := range m.app.Messages {
|
||||
switch msg.Role {
|
||||
case message.User:
|
||||
if cache, ok := m.cachedContent[msg.ID]; ok && cache.width == m.width {
|
||||
case client.User:
|
||||
if cache, ok := m.cachedContent[msg.Id]; ok && cache.width == m.width {
|
||||
m.uiMessages = append(m.uiMessages, cache.content...)
|
||||
continue
|
||||
}
|
||||
userMsg := renderUserMessage(
|
||||
msg,
|
||||
msg.ID == m.currentMsgID,
|
||||
msg.Id == m.currentMsgID,
|
||||
m.width,
|
||||
pos,
|
||||
)
|
||||
m.uiMessages = append(m.uiMessages, userMsg)
|
||||
m.cachedContent[msg.ID] = cacheItem{
|
||||
m.cachedContent[msg.Id] = cacheItem{
|
||||
width: m.width,
|
||||
content: []uiMessage{userMsg},
|
||||
}
|
||||
pos += userMsg.height + 1 // + 1 for spacing
|
||||
case message.Assistant:
|
||||
if cache, ok := m.cachedContent[msg.ID]; ok && cache.width == m.width {
|
||||
// pos += userMsg.height + 1 // + 1 for spacing
|
||||
case client.Assistant:
|
||||
if cache, ok := m.cachedContent[msg.Id]; ok && cache.width == m.width {
|
||||
m.uiMessages = append(m.uiMessages, cache.content...)
|
||||
continue
|
||||
}
|
||||
assistantMessages := renderAssistantMessage(
|
||||
msg,
|
||||
inx,
|
||||
m.messages,
|
||||
m.app.Messages,
|
||||
m.currentMsgID,
|
||||
m.width,
|
||||
pos,
|
||||
m.showToolMessages,
|
||||
)
|
||||
for _, msg := range assistantMessages {
|
||||
m.uiMessages = append(m.uiMessages, msg)
|
||||
pos += msg.height + 1 // + 1 for spacing
|
||||
}
|
||||
m.cachedContent[msg.ID] = cacheItem{
|
||||
width: m.width,
|
||||
content: assistantMessages,
|
||||
}
|
||||
// assistantMessages := renderAssistantMessage(
|
||||
// msg,
|
||||
// inx,
|
||||
// m.app.Messages,
|
||||
// m.app.MessagesOLD,
|
||||
// m.currentMsgID,
|
||||
// m.width,
|
||||
// pos,
|
||||
// m.showToolMessages,
|
||||
// )
|
||||
// for _, msg := range assistantMessages {
|
||||
// m.uiMessages = append(m.uiMessages, msg)
|
||||
// // pos += msg.height + 1 // + 1 for spacing
|
||||
// }
|
||||
// m.cachedContent[msg.Id] = cacheItem{
|
||||
// width: m.width,
|
||||
// content: assistantMessages,
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,33 +247,23 @@ func (m *messagesCmp) renderView() {
|
||||
)
|
||||
}
|
||||
|
||||
temp, _ := json.MarshalIndent(m.app.State, "", " ")
|
||||
// temp, _ := json.MarshalIndent(m.app.State, "", " ")
|
||||
|
||||
m.viewport.SetContent(
|
||||
baseStyle.
|
||||
Width(m.width).
|
||||
Render(
|
||||
string(temp),
|
||||
// lipgloss.JoinVertical(
|
||||
// lipgloss.Top,
|
||||
// messages...,
|
||||
// ),
|
||||
// string(temp),
|
||||
lipgloss.JoinVertical(
|
||||
lipgloss.Top,
|
||||
messages...,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (m *messagesCmp) View() string {
|
||||
baseStyle := styles.BaseStyle()
|
||||
return baseStyle.
|
||||
Width(m.width).
|
||||
Render(
|
||||
lipgloss.JoinVertical(
|
||||
lipgloss.Top,
|
||||
m.viewport.View(),
|
||||
m.working(),
|
||||
m.help(),
|
||||
),
|
||||
)
|
||||
|
||||
if m.rendering {
|
||||
return baseStyle.
|
||||
@@ -283,12 +272,12 @@ func (m *messagesCmp) View() string {
|
||||
lipgloss.JoinVertical(
|
||||
lipgloss.Top,
|
||||
"Loading...",
|
||||
m.working(),
|
||||
// m.working(),
|
||||
m.help(),
|
||||
),
|
||||
)
|
||||
}
|
||||
if len(m.messages) == 0 {
|
||||
if len(m.app.Messages) == 0 {
|
||||
content := baseStyle.
|
||||
Width(m.width).
|
||||
Height(m.height - 1).
|
||||
@@ -314,7 +303,7 @@ func (m *messagesCmp) View() string {
|
||||
lipgloss.JoinVertical(
|
||||
lipgloss.Top,
|
||||
m.viewport.View(),
|
||||
m.working(),
|
||||
// m.working(),
|
||||
m.help(),
|
||||
),
|
||||
)
|
||||
@@ -356,31 +345,31 @@ func hasUnfinishedToolCalls(messages []message.Message) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *messagesCmp) working() string {
|
||||
text := ""
|
||||
if m.IsAgentWorking() && len(m.messages) > 0 {
|
||||
t := theme.CurrentTheme()
|
||||
baseStyle := styles.BaseStyle()
|
||||
|
||||
task := "Thinking..."
|
||||
lastMessage := m.messages[len(m.messages)-1]
|
||||
if hasToolsWithoutResponse(m.messages) {
|
||||
task = "Waiting for tool response..."
|
||||
} else if hasUnfinishedToolCalls(m.messages) {
|
||||
task = "Building tool call..."
|
||||
} else if !lastMessage.IsFinished() {
|
||||
task = "Generating..."
|
||||
}
|
||||
if task != "" {
|
||||
text += baseStyle.
|
||||
Width(m.width).
|
||||
Foreground(t.Primary()).
|
||||
Bold(true).
|
||||
Render(fmt.Sprintf("%s %s ", m.spinner.View(), task))
|
||||
}
|
||||
}
|
||||
return text
|
||||
}
|
||||
// func (m *messagesCmp) working() string {
|
||||
// text := ""
|
||||
// if m.IsAgentWorking() && len(m.app.Messages) > 0 {
|
||||
// t := theme.CurrentTheme()
|
||||
// baseStyle := styles.BaseStyle()
|
||||
//
|
||||
// task := "Thinking..."
|
||||
// lastMessage := m.app.Messages[len(m.app.Messages)-1]
|
||||
// if hasToolsWithoutResponse(m.app.Messages) {
|
||||
// task = "Waiting for tool response..."
|
||||
// } else if hasUnfinishedToolCalls(m.app.Messages) {
|
||||
// task = "Building tool call..."
|
||||
// } else if !lastMessage.IsFinished() {
|
||||
// task = "Generating..."
|
||||
// }
|
||||
// if task != "" {
|
||||
// text += baseStyle.
|
||||
// Width(m.width).
|
||||
// Foreground(t.Primary()).
|
||||
// Bold(true).
|
||||
// Render(fmt.Sprintf("%s %s ", m.spinner.View(), task))
|
||||
// }
|
||||
// }
|
||||
// return text
|
||||
// }
|
||||
|
||||
func (m *messagesCmp) help() string {
|
||||
t := theme.CurrentTheme()
|
||||
@@ -388,7 +377,7 @@ func (m *messagesCmp) help() string {
|
||||
|
||||
text := ""
|
||||
|
||||
if m.app.PrimaryAgent.IsBusy() {
|
||||
if m.app.PrimaryAgentOLD.IsBusy() {
|
||||
text += lipgloss.JoinHorizontal(
|
||||
lipgloss.Left,
|
||||
baseStyle.Foreground(t.TextMuted()).Bold(true).Render("press "),
|
||||
@@ -429,8 +418,8 @@ func (m *messagesCmp) initialScreen() string {
|
||||
}
|
||||
|
||||
func (m *messagesCmp) rerender() {
|
||||
for _, msg := range m.messages {
|
||||
delete(m.cachedContent, msg.ID)
|
||||
for _, msg := range m.app.Messages {
|
||||
delete(m.cachedContent, msg.Id)
|
||||
}
|
||||
m.renderView()
|
||||
}
|
||||
@@ -454,14 +443,16 @@ func (m *messagesCmp) GetSize() (int, int) {
|
||||
}
|
||||
|
||||
func (m *messagesCmp) Reload(session *session.Session) tea.Cmd {
|
||||
messages, err := m.app.Messages.List(context.Background(), session.ID)
|
||||
if err != nil {
|
||||
status.Error(err.Error())
|
||||
return nil
|
||||
}
|
||||
m.messages = messages
|
||||
if len(m.messages) > 0 {
|
||||
m.currentMsgID = m.messages[len(m.messages)-1].ID
|
||||
// messages := m.app.Messages
|
||||
// messages, err := m.app.MessagesOLD.List(context.Background(), session.ID)
|
||||
// if err != nil {
|
||||
// status.Error(err.Error())
|
||||
// return nil
|
||||
// }
|
||||
// m.messages = messages
|
||||
|
||||
if len(m.app.Messages) > 0 {
|
||||
m.currentMsgID = m.app.Messages[len(m.app.Messages)-1].Id
|
||||
}
|
||||
delete(m.cachedContent, m.currentMsgID)
|
||||
m.rendering = true
|
||||
|
||||
@@ -86,7 +86,7 @@ func (m *sidebarCmp) sessionSection() string {
|
||||
|
||||
sessionValue := baseStyle.
|
||||
Foreground(t.Text()).
|
||||
Render(fmt.Sprintf(": %s", m.app.CurrentSession.Title))
|
||||
Render(fmt.Sprintf(": %s", m.app.CurrentSessionOLD.Title))
|
||||
|
||||
return sessionKey + sessionValue
|
||||
}
|
||||
@@ -209,7 +209,7 @@ func NewSidebarCmp(app *app.App) tea.Model {
|
||||
}
|
||||
|
||||
func (m *sidebarCmp) loadModifiedFiles(ctx context.Context) {
|
||||
if m.app.CurrentSession.ID == "" {
|
||||
if m.app.CurrentSessionOLD.ID == "" {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/sst/opencode/internal/config"
|
||||
"github.com/sst/opencode/internal/llm/models"
|
||||
"github.com/sst/opencode/internal/lsp"
|
||||
"github.com/sst/opencode/internal/lsp/protocol"
|
||||
"github.com/sst/opencode/internal/pubsub"
|
||||
"github.com/sst/opencode/internal/status"
|
||||
@@ -155,8 +154,8 @@ func (m statusCmp) View() string {
|
||||
// Initialize the help widget
|
||||
status := getHelpWidget("")
|
||||
|
||||
if m.app.CurrentSession.ID != "" {
|
||||
tokens := formatTokensAndCost(m.app.CurrentSession.PromptTokens+m.app.CurrentSession.CompletionTokens, model.ContextWindow, m.app.CurrentSession.Cost)
|
||||
if m.app.CurrentSessionOLD.ID != "" {
|
||||
tokens := formatTokensAndCost(m.app.CurrentSessionOLD.PromptTokens+m.app.CurrentSessionOLD.CompletionTokens, model.ContextWindow, m.app.CurrentSessionOLD.Cost)
|
||||
tokensStyle := styles.Padded().
|
||||
Background(t.Text()).
|
||||
Foreground(t.BackgroundSecondary()).
|
||||
@@ -245,12 +244,12 @@ func (m *statusCmp) projectDiagnostics() string {
|
||||
|
||||
// Check if any LSP server is still initializing
|
||||
initializing := false
|
||||
for _, client := range m.app.LSPClients {
|
||||
if client.GetServerState() == lsp.StateStarting {
|
||||
initializing = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// for _, client := range m.app.LSPClients {
|
||||
// if client.GetServerState() == lsp.StateStarting {
|
||||
// initializing = true
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
|
||||
// If any server is initializing, show that status
|
||||
if initializing {
|
||||
@@ -263,22 +262,22 @@ func (m *statusCmp) projectDiagnostics() string {
|
||||
warnDiagnostics := []protocol.Diagnostic{}
|
||||
hintDiagnostics := []protocol.Diagnostic{}
|
||||
infoDiagnostics := []protocol.Diagnostic{}
|
||||
for _, client := range m.app.LSPClients {
|
||||
for _, d := range client.GetDiagnostics() {
|
||||
for _, diag := range d {
|
||||
switch diag.Severity {
|
||||
case protocol.SeverityError:
|
||||
errorDiagnostics = append(errorDiagnostics, diag)
|
||||
case protocol.SeverityWarning:
|
||||
warnDiagnostics = append(warnDiagnostics, diag)
|
||||
case protocol.SeverityHint:
|
||||
hintDiagnostics = append(hintDiagnostics, diag)
|
||||
case protocol.SeverityInformation:
|
||||
infoDiagnostics = append(infoDiagnostics, diag)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// for _, client := range m.app.LSPClients {
|
||||
// for _, d := range client.GetDiagnostics() {
|
||||
// for _, diag := range d {
|
||||
// switch diag.Severity {
|
||||
// case protocol.SeverityError:
|
||||
// errorDiagnostics = append(errorDiagnostics, diag)
|
||||
// case protocol.SeverityWarning:
|
||||
// warnDiagnostics = append(warnDiagnostics, diag)
|
||||
// case protocol.SeverityHint:
|
||||
// hintDiagnostics = append(hintDiagnostics, diag)
|
||||
// case protocol.SeverityInformation:
|
||||
// infoDiagnostics = append(infoDiagnostics, diag)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if len(errorDiagnostics) == 0 &&
|
||||
len(warnDiagnostics) == 0 &&
|
||||
|
||||
@@ -78,7 +78,7 @@ func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
case dialog.CommandRunCustomMsg:
|
||||
// Check if the agent is busy before executing custom commands
|
||||
if p.app.PrimaryAgent.IsBusy() {
|
||||
if p.app.PrimaryAgentOLD.IsBusy() {
|
||||
status.Warn("Agent is busy, please wait before executing a command...")
|
||||
return p, nil
|
||||
}
|
||||
@@ -105,20 +105,20 @@ func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
cmd := p.setSidebar()
|
||||
cmds = append(cmds, cmd)
|
||||
case state.CompactSessionMsg:
|
||||
if p.app.CurrentSession.ID == "" {
|
||||
if p.app.CurrentSessionOLD.ID == "" {
|
||||
status.Warn("No active session to compact.")
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// Run compaction in background
|
||||
go func(sessionID string) {
|
||||
err := p.app.PrimaryAgent.CompactSession(context.Background(), sessionID, false)
|
||||
err := p.app.PrimaryAgentOLD.CompactSession(context.Background(), sessionID, false)
|
||||
if err != nil {
|
||||
status.Error(fmt.Sprintf("Compaction failed: %v", err))
|
||||
} else {
|
||||
status.Info("Conversation compacted successfully.")
|
||||
}
|
||||
}(p.app.CurrentSession.ID)
|
||||
}(p.app.CurrentSessionOLD.ID)
|
||||
|
||||
return p, nil
|
||||
case dialog.CompletionDialogCloseMsg:
|
||||
@@ -131,16 +131,16 @@ func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
p.app.SetCompletionDialogOpen(true)
|
||||
// Continue sending keys to layout->chat
|
||||
case key.Matches(msg, keyMap.NewSession):
|
||||
p.app.CurrentSession = &session.Session{}
|
||||
p.app.CurrentSessionOLD = &session.Session{}
|
||||
return p, tea.Batch(
|
||||
p.clearSidebar(),
|
||||
util.CmdHandler(state.SessionClearedMsg{}),
|
||||
)
|
||||
case key.Matches(msg, keyMap.Cancel):
|
||||
if p.app.CurrentSession.ID != "" {
|
||||
if p.app.CurrentSessionOLD.ID != "" {
|
||||
// Cancel the current session's generation process
|
||||
// This allows users to interrupt long-running operations
|
||||
p.app.PrimaryAgent.Cancel(p.app.CurrentSession.ID)
|
||||
p.app.PrimaryAgentOLD.Cancel(p.app.CurrentSessionOLD.ID)
|
||||
return p, nil
|
||||
}
|
||||
case key.Matches(msg, keyMap.ToggleTools):
|
||||
|
||||
@@ -2,6 +2,7 @@ package tui
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
@@ -178,7 +179,7 @@ func (a appModel) Init() tea.Cmd {
|
||||
func (a appModel) updateAllPages(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
var cmds []tea.Cmd
|
||||
var cmd tea.Cmd
|
||||
for id, _ := range a.pages {
|
||||
for id := range a.pages {
|
||||
a.pages[id], cmd = a.pages[id].Update(msg)
|
||||
cmds = append(cmds, cmd)
|
||||
}
|
||||
@@ -256,26 +257,76 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return a, a.moveToPage(msg.ID)
|
||||
|
||||
case state.SessionSelectedMsg:
|
||||
a.app.CurrentSession = msg
|
||||
a.app.CurrentSessionOLD = msg
|
||||
return a.updateAllPages(msg)
|
||||
|
||||
case pubsub.Event[session.Session]:
|
||||
if msg.Type == session.EventSessionUpdated {
|
||||
if a.app.CurrentSession.ID == msg.Payload.ID {
|
||||
a.app.CurrentSession = &msg.Payload
|
||||
if a.app.CurrentSessionOLD.ID == msg.Payload.ID {
|
||||
a.app.CurrentSessionOLD = &msg.Payload
|
||||
}
|
||||
}
|
||||
|
||||
// Handle SSE events from the TypeScript backend
|
||||
case client.EventStorageWrite:
|
||||
slog.Debug("Received SSE event", "key", msg.Properties.Key)
|
||||
parts := strings.Split(msg.Key, "/")
|
||||
if len(parts) < 3 {
|
||||
return a, nil
|
||||
}
|
||||
|
||||
if parts[0] == "session" && parts[1] == "info" {
|
||||
sessionId := parts[2]
|
||||
if sessionId == a.app.Session.Id {
|
||||
var sessionInfo client.SessionInfo
|
||||
bytes, _ := json.Marshal(msg.Content)
|
||||
if err := json.Unmarshal(bytes, &sessionInfo); err != nil {
|
||||
status.Error(err.Error())
|
||||
return a, nil
|
||||
}
|
||||
|
||||
a.app.Session = &sessionInfo
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
if parts[0] == "session" && parts[1] == "message" {
|
||||
sessionId := parts[2]
|
||||
if sessionId == a.app.Session.Id {
|
||||
messageId := parts[3]
|
||||
var message client.SessionMessage
|
||||
bytes, _ := json.Marshal(msg.Content)
|
||||
if err := json.Unmarshal(bytes, &message); err != nil {
|
||||
status.Error(err.Error())
|
||||
return a, nil
|
||||
}
|
||||
|
||||
for i, m := range a.app.Messages {
|
||||
if m.Id == messageId {
|
||||
a.app.Messages[i] = message
|
||||
slog.Debug("Updated message", "message", message)
|
||||
return a, nil
|
||||
}
|
||||
}
|
||||
|
||||
a.app.Messages = append(a.app.Messages, message)
|
||||
slog.Debug("Appended message", "message", message)
|
||||
|
||||
// a.app.CurrentSession.MessageCount++
|
||||
// a.app.CurrentSession.PromptTokens += message.PromptTokens
|
||||
// a.app.CurrentSession.CompletionTokens += message.CompletionTokens
|
||||
// a.app.CurrentSession.Cost += message.Cost
|
||||
// a.app.CurrentSession.UpdatedAt = message.CreatedAt
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
// log key and content
|
||||
slog.Debug("Received SSE event", "key", msg.Key, "content", msg.Content)
|
||||
|
||||
splits := strings.Split(msg.Properties.Key, "/")
|
||||
current := a.app.State
|
||||
|
||||
for i, part := range splits {
|
||||
if i == len(splits)-1 {
|
||||
current[part] = msg.Properties.Content
|
||||
for i, part := range parts {
|
||||
if i == len(parts)-1 {
|
||||
current[part] = msg.Content
|
||||
} else {
|
||||
if _, exists := current[part]; !exists {
|
||||
current[part] = make(map[string]any)
|
||||
@@ -566,7 +617,7 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
return a, nil
|
||||
case key.Matches(msg, helpEsc):
|
||||
if a.app.PrimaryAgent.IsBusy() {
|
||||
if a.app.PrimaryAgentOLD.IsBusy() {
|
||||
if a.showQuit {
|
||||
return a, nil
|
||||
}
|
||||
@@ -705,27 +756,9 @@ func (a *appModel) RegisterCommand(cmd dialog.Command) {
|
||||
}
|
||||
|
||||
// getAvailableToolNames returns a list of all available tool names
|
||||
func getAvailableToolNames(app *app.App) []string {
|
||||
func getAvailableToolNames(_ *app.App) []string {
|
||||
// TODO: Tools not implemented in API yet
|
||||
return []string{"Tools not available in API mode"}
|
||||
/*
|
||||
// Get primary agent tools (which already include MCP tools)
|
||||
allTools := agent.PrimaryAgentTools(
|
||||
app.Permissions,
|
||||
app.Sessions,
|
||||
app.Messages,
|
||||
app.History,
|
||||
app.LSPClients,
|
||||
)
|
||||
|
||||
// Extract tool names
|
||||
var toolNames []string
|
||||
for _, tool := range allTools {
|
||||
toolNames = append(toolNames, tool.Info().Name)
|
||||
}
|
||||
|
||||
return toolNames
|
||||
*/
|
||||
}
|
||||
|
||||
func (a *appModel) moveToPage(pageID page.PageID) tea.Cmd {
|
||||
@@ -785,7 +818,7 @@ func (a appModel) View() string {
|
||||
|
||||
}
|
||||
|
||||
if !a.app.PrimaryAgent.IsBusy() {
|
||||
if !a.app.PrimaryAgentOLD.IsBusy() {
|
||||
a.status.SetHelpWidgetMsg("ctrl+? help")
|
||||
} else {
|
||||
a.status.SetHelpWidgetMsg("? help")
|
||||
@@ -799,7 +832,7 @@ func (a appModel) View() string {
|
||||
if a.showPermissions {
|
||||
bindings = append(bindings, a.permissions.BindingKeys()...)
|
||||
}
|
||||
if !a.app.PrimaryAgent.IsBusy() {
|
||||
if !a.app.PrimaryAgentOLD.IsBusy() {
|
||||
bindings = append(bindings, helpEsc)
|
||||
}
|
||||
a.help.SetBindings(bindings)
|
||||
|
||||
Reference in New Issue
Block a user