summaryrefslogtreecommitdiffhomepage
path: root/internal/app
diff options
context:
space:
mode:
authoradamdottv <[email protected]>2025-05-28 12:35:20 -0500
committeradamdottv <[email protected]>2025-05-28 15:36:35 -0500
commit15d21bf04acd6af75df97918f66df866c239b248 (patch)
tree16ac93b0c686faaf3b21700771b082b6bc26f098 /internal/app
parent5e738ce7d3b66b88a407a1bcc53a5169d0f4a904 (diff)
downloadopencode-15d21bf04acd6af75df97918f66df866c239b248.tar.gz
opencode-15d21bf04acd6af75df97918f66df866c239b248.zip
wip: refactoring tui
Diffstat (limited to 'internal/app')
-rw-r--r--internal/app/app.go35
-rw-r--r--internal/app/event_adapter.go158
-rw-r--r--internal/app/services_bridge.go19
3 files changed, 33 insertions, 179 deletions
diff --git a/internal/app/app.go b/internal/app/app.go
index 7d0bdd4cb..7aed6fb10 100644
--- a/internal/app/app.go
+++ b/internal/app/app.go
@@ -18,14 +18,17 @@ import (
)
type App struct {
+ State map[string]any
+
CurrentSession *session.Session
- Logs interface{} // TODO: Define LogService interface when needed
+ Logs any // TODO: Define LogService interface when needed
Sessions SessionService
Messages MessageService
- History interface{} // TODO: Define HistoryService interface when needed
- Permissions interface{} // TODO: Define PermissionService interface when needed
+ History any // TODO: Define HistoryService interface when needed
+ Permissions any // TODO: Define PermissionService interface when needed
Status status.Service
- Client *client.Client
+ Client *client.ClientWithResponses
+ Events *client.Client
PrimaryAgent AgentService
@@ -36,9 +39,9 @@ type App struct {
watcherCancelFuncs []context.CancelFunc
cancelFuncsMutex sync.Mutex
watcherWG sync.WaitGroup
-
+
// UI state
- filepickerOpen bool
+ filepickerOpen bool
completionDialogOpen bool
}
@@ -49,16 +52,22 @@ func New(ctx context.Context) (*App, error) {
slog.Error("Failed to initialize status service", "error", err)
return nil, err
}
-
+
// Initialize file utilities
fileutil.Init()
// Create HTTP client
- httpClient, err := client.NewClient("http://localhost:16713")
+ url := "http://localhost:16713"
+ httpClient, err := client.NewClientWithResponses(url)
if err != nil {
slog.Error("Failed to create client", "error", err)
return nil, err
}
+ eventClient, err := client.NewClient(url)
+ if err != nil {
+ slog.Error("Failed to create event client", "error", err)
+ return nil, err
+ }
// Create service bridges
sessionBridge := NewSessionServiceBridge(httpClient)
@@ -66,18 +75,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),
-
+
// TODO: These services need API endpoints:
- Logs: nil, // logging.GetService(),
- History: nil, // history.GetService(),
- Permissions: nil, // permission.GetService(),
+ Logs: nil, // logging.GetService(),
+ History: nil, // history.GetService(),
+ Permissions: nil, // permission.GetService(),
}
// Initialize theme based on configuration
diff --git a/internal/app/event_adapter.go b/internal/app/event_adapter.go
deleted file mode 100644
index 4772bde49..000000000
--- a/internal/app/event_adapter.go
+++ /dev/null
@@ -1,158 +0,0 @@
-package app
-
-import (
- "encoding/json"
- "time"
-
- tea "github.com/charmbracelet/bubbletea"
- "github.com/sst/opencode/internal/message"
- "github.com/sst/opencode/pkg/client"
-)
-
-// StorageWriteMsg is sent when a storage.write event is received
-type StorageWriteMsg struct {
- Key string
- Content interface{}
-}
-
-// ProcessSSEEvent converts SSE events into TUI messages
-func ProcessSSEEvent(event interface{}) tea.Msg {
- switch e := event.(type) {
- case *client.EventStorageWrite:
- return StorageWriteMsg{
- Key: e.Key,
- Content: e.Content,
- }
- }
-
- // Return the raw event if we don't have a specific handler
- return event
-}
-
-// MessageFromStorage converts storage content to internal message format
-type MessageData struct {
- ID string `json:"id"`
- Role string `json:"role"`
- Parts []interface{} `json:"parts"`
- Metadata map[string]interface{} `json:"metadata"`
-}
-
-// SessionInfoFromStorage converts storage content to session info
-type SessionInfoData struct {
- ID string `json:"id"`
- Title string `json:"title"`
- ShareID *string `json:"shareID,omitempty"`
- Tokens struct {
- Input float32 `json:"input"`
- Output float32 `json:"output"`
- Reasoning float32 `json:"reasoning"`
- } `json:"tokens"`
-}
-
-// ConvertStorageMessage converts a storage message to internal message format
-func ConvertStorageMessage(data interface{}, sessionID string) (*message.Message, error) {
- // Convert the interface{} to JSON then back to our struct
- jsonData, err := json.Marshal(data)
- if err != nil {
- return nil, err
- }
-
- var msgData MessageData
- if err := json.Unmarshal(jsonData, &msgData); err != nil {
- return nil, err
- }
-
- // Convert parts
- var parts []message.ContentPart
- for _, part := range msgData.Parts {
- partMap, ok := part.(map[string]interface{})
- if !ok {
- continue
- }
-
- partType, ok := partMap["type"].(string)
- if !ok {
- continue
- }
-
- switch partType {
- case "text":
- if text, ok := partMap["text"].(string); ok {
- parts = append(parts, message.TextContent{Text: text})
- }
- case "tool-invocation":
- if toolInv, ok := partMap["toolInvocation"].(map[string]interface{}); ok {
- // Convert tool invocation to tool call
- toolCall := message.ToolCall{
- ID: toolInv["toolCallId"].(string),
- Name: toolInv["toolName"].(string),
- Type: "function",
- }
-
- if args, ok := toolInv["args"]; ok {
- argsJSON, _ := json.Marshal(args)
- toolCall.Input = string(argsJSON)
- }
-
- if state, ok := toolInv["state"].(string); ok {
- toolCall.Finished = state == "result"
- }
-
- parts = append(parts, toolCall)
-
- // If there's a result, add it as a tool result
- if result, ok := toolInv["result"]; ok && toolCall.Finished {
- resultStr := ""
- switch r := result.(type) {
- case string:
- resultStr = r
- default:
- resultJSON, _ := json.Marshal(r)
- resultStr = string(resultJSON)
- }
-
- parts = append(parts, message.ToolResult{
- ToolCallID: toolCall.ID,
- Name: toolCall.Name,
- Content: resultStr,
- })
- }
- }
- }
- }
-
- // Convert role
- var role message.MessageRole
- switch msgData.Role {
- case "user":
- role = message.User
- case "assistant":
- role = message.Assistant
- case "system":
- role = message.System
- default:
- role = message.MessageRole(msgData.Role)
- }
-
- // Create message
- msg := &message.Message{
- ID: msgData.ID,
- Role: role,
- SessionID: sessionID,
- Parts: parts,
- CreatedAt: time.Now(), // TODO: Get from metadata
- UpdatedAt: time.Now(), // TODO: Get from metadata
- }
-
- // Try to get timestamps from metadata
- if metadata, ok := msgData.Metadata["time"].(map[string]interface{}); ok {
- if created, ok := metadata["created"].(float64); ok {
- msg.CreatedAt = time.Unix(int64(created/1000), 0)
- }
- if completed, ok := metadata["completed"].(float64); ok {
- msg.UpdatedAt = time.Unix(int64(completed/1000), 0)
- }
- }
-
- return msg, nil
-} \ No newline at end of file
diff --git a/internal/app/services_bridge.go b/internal/app/services_bridge.go
index e2d2ab5ba..d7e032bfd 100644
--- a/internal/app/services_bridge.go
+++ b/internal/app/services_bridge.go
@@ -14,11 +14,11 @@ import (
// SessionServiceBridge adapts the HTTP API to the old session.Service interface
type SessionServiceBridge struct {
- client *client.Client
+ client *client.ClientWithResponses
}
// NewSessionServiceBridge creates a new session service bridge
-func NewSessionServiceBridge(client *client.Client) *SessionServiceBridge {
+func NewSessionServiceBridge(client *client.ClientWithResponses) *SessionServiceBridge {
return &SessionServiceBridge{client: client}
}
@@ -107,11 +107,11 @@ func (s *SessionServiceBridge) Delete(ctx context.Context, id string) error {
// AgentServiceBridge provides a minimal agent service that sends messages to the API
type AgentServiceBridge struct {
- client *client.Client
+ client *client.ClientWithResponses
}
// NewAgentServiceBridge creates a new agent service bridge
-func NewAgentServiceBridge(client *client.Client) *AgentServiceBridge {
+func NewAgentServiceBridge(client *client.ClientWithResponses) *AgentServiceBridge {
return &AgentServiceBridge{client: client}
}
@@ -123,7 +123,7 @@ func (a *AgentServiceBridge) Run(ctx context.Context, sessionID string, text str
// return "", fmt.Errorf("attachments not supported yet")
}
- parts := interface{}([]map[string]interface{}{
+ parts := any([]map[string]any{
{
"type": "text",
"text": text,
@@ -170,12 +170,12 @@ func (a *AgentServiceBridge) CompactSession(ctx context.Context, sessionID strin
// MessageServiceBridge provides a minimal message service that fetches from the API
type MessageServiceBridge struct {
- client *client.Client
+ client *client.ClientWithResponses
broker *pubsub.Broker[message.Message]
}
// NewMessageServiceBridge creates a new message service bridge
-func NewMessageServiceBridge(client *client.Client) *MessageServiceBridge {
+func NewMessageServiceBridge(client *client.ClientWithResponses) *MessageServiceBridge {
return &MessageServiceBridge{
client: client,
broker: pubsub.NewBroker[message.Message](),
@@ -198,7 +198,7 @@ func (m *MessageServiceBridge) List(ctx context.Context, sessionID string) ([]me
defer resp.Body.Close()
// The API returns a different format, we'll need to adapt it
- var rawMessages interface{}
+ var rawMessages any
if err := json.NewDecoder(resp.Body).Decode(&rawMessages); err != nil {
return nil, err
}
@@ -247,4 +247,5 @@ func (m *MessageServiceBridge) ListAfter(ctx context.Context, sessionID string,
// Subscribe subscribes to message events
func (m *MessageServiceBridge) Subscribe(ctx context.Context) <-chan pubsub.Event[message.Message] {
return m.broker.Subscribe(ctx)
-} \ No newline at end of file
+}
+