mirror of
https://github.com/aljazceru/opencode.git
synced 2026-01-08 10:24:52 +01:00
small fixes
This commit is contained in:
@@ -21,6 +21,8 @@ type editorCmp struct {
|
||||
textarea textarea.Model
|
||||
}
|
||||
|
||||
type FocusEditorMsg bool
|
||||
|
||||
type focusedEditorKeyMaps struct {
|
||||
Send key.Binding
|
||||
OpenEditor key.Binding
|
||||
@@ -112,7 +114,6 @@ func (m *editorCmp) send() tea.Cmd {
|
||||
util.CmdHandler(SendMsg{
|
||||
Text: value,
|
||||
}),
|
||||
util.CmdHandler(EditorFocusMsg(false)),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -124,9 +125,13 @@ func (m *editorCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.session = msg
|
||||
}
|
||||
return m, nil
|
||||
case FocusEditorMsg:
|
||||
if msg {
|
||||
m.textarea.Focus()
|
||||
return m, tea.Batch(textarea.Blink, util.CmdHandler(EditorFocusMsg(true)))
|
||||
}
|
||||
case tea.KeyMsg:
|
||||
if key.Matches(msg, focusedKeyMaps.OpenEditor) {
|
||||
m.textarea.Blur()
|
||||
return m, openEditor()
|
||||
}
|
||||
// if the key does not match any binding, return
|
||||
|
||||
@@ -22,6 +22,10 @@ import (
|
||||
"github.com/kujtimiihoxha/opencode/internal/tui/util"
|
||||
)
|
||||
|
||||
type cacheItem struct {
|
||||
width int
|
||||
content []uiMessage
|
||||
}
|
||||
type messagesCmp struct {
|
||||
app *app.App
|
||||
width, height int
|
||||
@@ -32,8 +36,9 @@ type messagesCmp struct {
|
||||
uiMessages []uiMessage
|
||||
currentMsgID string
|
||||
mutex sync.Mutex
|
||||
cachedContent map[string][]uiMessage
|
||||
cachedContent map[string]cacheItem
|
||||
spinner spinner.Model
|
||||
lastUpdate time.Time
|
||||
rendering bool
|
||||
}
|
||||
type renderFinishedMsg struct{}
|
||||
@@ -44,6 +49,8 @@ func (m *messagesCmp) Init() tea.Cmd {
|
||||
|
||||
func (m *messagesCmp) preloadSessions() tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
sessions, err := m.app.Sessions.List(context.Background())
|
||||
if err != nil {
|
||||
return util.ReportError(err)()
|
||||
@@ -67,13 +74,13 @@ func (m *messagesCmp) preloadSessions() tea.Cmd {
|
||||
}
|
||||
logging.Debug("preloaded sessions")
|
||||
|
||||
return nil
|
||||
return func() tea.Msg {
|
||||
return renderFinishedMsg{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *messagesCmp) cacheSessionMessages(messages []message.Message, width int) {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
pos := 0
|
||||
if m.width == 0 {
|
||||
return
|
||||
@@ -87,7 +94,10 @@ func (m *messagesCmp) cacheSessionMessages(messages []message.Message, width int
|
||||
width,
|
||||
pos,
|
||||
)
|
||||
m.cachedContent[msg.ID] = []uiMessage{userMsg}
|
||||
m.cachedContent[msg.ID] = cacheItem{
|
||||
width: width,
|
||||
content: []uiMessage{userMsg},
|
||||
}
|
||||
pos += userMsg.height + 1 // + 1 for spacing
|
||||
case message.Assistant:
|
||||
assistantMessages := renderAssistantMessage(
|
||||
@@ -102,7 +112,10 @@ func (m *messagesCmp) cacheSessionMessages(messages []message.Message, width int
|
||||
for _, msg := range assistantMessages {
|
||||
pos += msg.height + 1 // + 1 for spacing
|
||||
}
|
||||
m.cachedContent[msg.ID] = assistantMessages
|
||||
m.cachedContent[msg.ID] = cacheItem{
|
||||
width: width,
|
||||
content: assistantMessages,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,8 +236,8 @@ func (m *messagesCmp) renderView() {
|
||||
for inx, msg := range m.messages {
|
||||
switch msg.Role {
|
||||
case message.User:
|
||||
if messages, ok := m.cachedContent[msg.ID]; ok {
|
||||
m.uiMessages = append(m.uiMessages, messages...)
|
||||
if cache, ok := m.cachedContent[msg.ID]; ok && cache.width == m.width {
|
||||
m.uiMessages = append(m.uiMessages, cache.content...)
|
||||
continue
|
||||
}
|
||||
userMsg := renderUserMessage(
|
||||
@@ -234,11 +247,14 @@ func (m *messagesCmp) renderView() {
|
||||
pos,
|
||||
)
|
||||
m.uiMessages = append(m.uiMessages, userMsg)
|
||||
m.cachedContent[msg.ID] = []uiMessage{userMsg}
|
||||
m.cachedContent[msg.ID] = cacheItem{
|
||||
width: m.width,
|
||||
content: []uiMessage{userMsg},
|
||||
}
|
||||
pos += userMsg.height + 1 // + 1 for spacing
|
||||
case message.Assistant:
|
||||
if messages, ok := m.cachedContent[msg.ID]; ok {
|
||||
m.uiMessages = append(m.uiMessages, messages...)
|
||||
if cache, ok := m.cachedContent[msg.ID]; ok && cache.width == m.width {
|
||||
m.uiMessages = append(m.uiMessages, cache.content...)
|
||||
continue
|
||||
}
|
||||
assistantMessages := renderAssistantMessage(
|
||||
@@ -254,7 +270,10 @@ func (m *messagesCmp) renderView() {
|
||||
m.uiMessages = append(m.uiMessages, msg)
|
||||
pos += msg.height + 1 // + 1 for spacing
|
||||
}
|
||||
m.cachedContent[msg.ID] = assistantMessages
|
||||
m.cachedContent[msg.ID] = cacheItem{
|
||||
width: m.width,
|
||||
content: assistantMessages,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,6 +437,10 @@ func (m *messagesCmp) SetSize(width, height int) tea.Cmd {
|
||||
m.height = height
|
||||
m.viewport.Width = width
|
||||
m.viewport.Height = height - 2
|
||||
for _, msg := range m.messages {
|
||||
delete(m.cachedContent, msg.ID)
|
||||
}
|
||||
m.uiMessages = make([]uiMessage, 0)
|
||||
m.renderView()
|
||||
return m.preloadSessions()
|
||||
}
|
||||
@@ -456,7 +479,7 @@ func NewMessagesCmp(app *app.App) tea.Model {
|
||||
return &messagesCmp{
|
||||
app: app,
|
||||
writingMode: true,
|
||||
cachedContent: make(map[string][]uiMessage),
|
||||
cachedContent: make(map[string]cacheItem),
|
||||
viewport: viewport.New(0, 0),
|
||||
spinner: s,
|
||||
}
|
||||
|
||||
@@ -389,6 +389,7 @@ func renderToolResponse(toolCall message.ToolCall, response message.ToolResult,
|
||||
errContent := fmt.Sprintf("Error: %s", strings.ReplaceAll(response.Content, "\n", " "))
|
||||
errContent = ansi.Truncate(errContent, width-1, "...")
|
||||
return styles.BaseStyle.
|
||||
Width(width).
|
||||
Foreground(styles.Error).
|
||||
Render(errContent)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,8 @@ type PermissionDialogCmp interface {
|
||||
}
|
||||
|
||||
type permissionsMapping struct {
|
||||
LeftRight key.Binding
|
||||
Left key.Binding
|
||||
Right key.Binding
|
||||
EnterSpace key.Binding
|
||||
Allow key.Binding
|
||||
AllowSession key.Binding
|
||||
@@ -49,9 +50,13 @@ type permissionsMapping struct {
|
||||
}
|
||||
|
||||
var permissionsKeys = permissionsMapping{
|
||||
LeftRight: key.NewBinding(
|
||||
key.WithKeys("left", "right"),
|
||||
key.WithHelp("←/→", "switch options"),
|
||||
Left: key.NewBinding(
|
||||
key.WithKeys("left"),
|
||||
key.WithHelp("←", "switch options"),
|
||||
),
|
||||
Right: key.NewBinding(
|
||||
key.WithKeys("right"),
|
||||
key.WithHelp("→", "switch options"),
|
||||
),
|
||||
EnterSpace: key.NewBinding(
|
||||
key.WithKeys("enter", " "),
|
||||
@@ -104,21 +109,18 @@ func (p *permissionDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
p.diffCache = make(map[string]string)
|
||||
case tea.KeyMsg:
|
||||
switch {
|
||||
case key.Matches(msg, permissionsKeys.LeftRight) || key.Matches(msg, permissionsKeys.Tab):
|
||||
// Change selected option
|
||||
case key.Matches(msg, permissionsKeys.Right) || key.Matches(msg, permissionsKeys.Tab):
|
||||
p.selectedOption = (p.selectedOption + 1) % 3
|
||||
return p, nil
|
||||
case key.Matches(msg, permissionsKeys.Left):
|
||||
p.selectedOption = (p.selectedOption + 2) % 3
|
||||
case key.Matches(msg, permissionsKeys.EnterSpace):
|
||||
// Select current option
|
||||
return p, p.selectCurrentOption()
|
||||
case key.Matches(msg, permissionsKeys.Allow):
|
||||
// Select Allow
|
||||
return p, util.CmdHandler(PermissionResponseMsg{Action: PermissionAllow, Permission: p.permission})
|
||||
case key.Matches(msg, permissionsKeys.AllowSession):
|
||||
// Select Allow for session
|
||||
return p, util.CmdHandler(PermissionResponseMsg{Action: PermissionAllowForSession, Permission: p.permission})
|
||||
case key.Matches(msg, permissionsKeys.Deny):
|
||||
// Select Deny
|
||||
return p, util.CmdHandler(PermissionResponseMsg{Action: PermissionDeny, Permission: p.permission})
|
||||
default:
|
||||
// Pass other keys to viewport
|
||||
|
||||
@@ -27,20 +27,20 @@ type SessionDialog interface {
|
||||
}
|
||||
|
||||
type sessionDialogCmp struct {
|
||||
sessions []session.Session
|
||||
selectedIdx int
|
||||
width int
|
||||
height int
|
||||
sessions []session.Session
|
||||
selectedIdx int
|
||||
width int
|
||||
height int
|
||||
selectedSessionID string
|
||||
}
|
||||
|
||||
type sessionKeyMap struct {
|
||||
Up key.Binding
|
||||
Down key.Binding
|
||||
Enter key.Binding
|
||||
Escape key.Binding
|
||||
J key.Binding
|
||||
K key.Binding
|
||||
Up key.Binding
|
||||
Down key.Binding
|
||||
Enter key.Binding
|
||||
Escape key.Binding
|
||||
J key.Binding
|
||||
K key.Binding
|
||||
}
|
||||
|
||||
var sessionKeys = sessionKeyMap{
|
||||
@@ -128,7 +128,7 @@ func (s *sessionDialogCmp) View() string {
|
||||
// Build the session list
|
||||
sessionItems := make([]string, 0, maxVisibleSessions)
|
||||
startIdx := 0
|
||||
|
||||
|
||||
// If we have more sessions than can be displayed, adjust the start index
|
||||
if len(s.sessions) > maxVisibleSessions {
|
||||
// Center the selected item when possible
|
||||
@@ -145,30 +145,31 @@ func (s *sessionDialogCmp) View() string {
|
||||
for i := startIdx; i < endIdx; i++ {
|
||||
sess := s.sessions[i]
|
||||
itemStyle := styles.BaseStyle.Width(maxWidth)
|
||||
|
||||
|
||||
if i == s.selectedIdx {
|
||||
itemStyle = itemStyle.
|
||||
Background(styles.PrimaryColor).
|
||||
Foreground(styles.Background).
|
||||
Bold(true)
|
||||
}
|
||||
|
||||
|
||||
sessionItems = append(sessionItems, itemStyle.Padding(0, 1).Render(sess.Title))
|
||||
}
|
||||
|
||||
title := styles.BaseStyle.
|
||||
Foreground(styles.PrimaryColor).
|
||||
Bold(true).
|
||||
Width(maxWidth).
|
||||
Padding(0, 1).
|
||||
Render("Switch Session")
|
||||
|
||||
content := lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
title,
|
||||
styles.BaseStyle.Render(""),
|
||||
lipgloss.JoinVertical(lipgloss.Left, sessionItems...),
|
||||
styles.BaseStyle.Render(""),
|
||||
styles.BaseStyle.Foreground(styles.ForgroundDim).Render("↑/k: up ↓/j: down enter: select esc: cancel"),
|
||||
styles.BaseStyle.Width(maxWidth).Render(""),
|
||||
styles.BaseStyle.Width(maxWidth).Render(lipgloss.JoinVertical(lipgloss.Left, sessionItems...)),
|
||||
styles.BaseStyle.Width(maxWidth).Render(""),
|
||||
styles.BaseStyle.Width(maxWidth).Padding(0, 1).Foreground(styles.ForgroundDim).Render("↑/k: up ↓/j: down enter: select esc: cancel"),
|
||||
)
|
||||
|
||||
return styles.BaseStyle.Padding(1, 2).
|
||||
@@ -185,7 +186,7 @@ func (s *sessionDialogCmp) BindingKeys() []key.Binding {
|
||||
|
||||
func (s *sessionDialogCmp) SetSessions(sessions []session.Session) {
|
||||
s.sessions = sessions
|
||||
|
||||
|
||||
// If we have a selected session ID, find its index
|
||||
if s.selectedSessionID != "" {
|
||||
for i, sess := range sessions {
|
||||
@@ -195,14 +196,14 @@ func (s *sessionDialogCmp) SetSessions(sessions []session.Session) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Default to first session if selected not found
|
||||
s.selectedIdx = 0
|
||||
}
|
||||
|
||||
func (s *sessionDialogCmp) SetSelectedSession(sessionID string) {
|
||||
s.selectedSessionID = sessionID
|
||||
|
||||
|
||||
// Update the selected index if sessions are already loaded
|
||||
if len(s.sessions) > 0 {
|
||||
for i, sess := range s.sessions {
|
||||
@@ -217,8 +218,9 @@ func (s *sessionDialogCmp) SetSelectedSession(sessionID string) {
|
||||
// NewSessionDialogCmp creates a new session switching dialog
|
||||
func NewSessionDialogCmp() SessionDialog {
|
||||
return &sessionDialogCmp{
|
||||
sessions: []session.Session{},
|
||||
selectedIdx: 0,
|
||||
sessions: []session.Session{},
|
||||
selectedIdx: 0,
|
||||
selectedSessionID: "",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,13 +43,6 @@ func (p *chatPage) Init() tea.Cmd {
|
||||
cmds := []tea.Cmd{
|
||||
p.layout.Init(),
|
||||
}
|
||||
|
||||
sessions, _ := p.app.Sessions.List(context.Background())
|
||||
if len(sessions) > 0 {
|
||||
p.session = sessions[0]
|
||||
cmd := p.setSidebar()
|
||||
cmds = append(cmds, util.CmdHandler(chat.SessionSelectedMsg(p.session)), cmd)
|
||||
}
|
||||
return tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
|
||||
@@ -163,6 +163,7 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
a.showPermissions = true
|
||||
return a, a.permissions.SetPermissions(msg.Payload)
|
||||
case dialog.PermissionResponseMsg:
|
||||
var cmd tea.Cmd
|
||||
switch msg.Action {
|
||||
case dialog.PermissionAllow:
|
||||
a.app.Permissions.Grant(msg.Permission)
|
||||
@@ -170,9 +171,10 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
a.app.Permissions.GrantPersistant(msg.Permission)
|
||||
case dialog.PermissionDeny:
|
||||
a.app.Permissions.Deny(msg.Permission)
|
||||
cmd = util.CmdHandler(chat.FocusEditorMsg(true))
|
||||
}
|
||||
a.showPermissions = false
|
||||
return a, nil
|
||||
return a, cmd
|
||||
|
||||
case page.PageChangeMsg:
|
||||
return a, a.moveToPage(msg.ID)
|
||||
|
||||
Reference in New Issue
Block a user